使用資訊清單轉移特定檔案或物件

Storage 移轉服務支援移轉特定檔案或物件,這些檔案或物件是使用資訊清單指定。資訊清單是上傳至 Cloud Storage 的 CSV 檔案,內含 Storage 移轉服務要處理的檔案或物件清單。

資訊清單可用於下列移轉作業:

  • 從 AWS S3、與 S3 相容的儲存空間、Azure Blobstore 或 Cloud Storage 移至 Cloud Storage bucket。

  • 從檔案系統到 Cloud Storage bucket。

  • 從 Cloud Storage bucket 移至檔案系統。

  • 在兩個檔案系統之間。

  • 從可公開存取的 HTTP/HTTPS 來源傳輸至 Cloud Storage bucket。請按照「建立網址清單」中的操作說明進行,因為網址清單有專屬的資訊清單格式。

建立資訊清單

資訊清單檔案必須符合下列規定:

  • 資訊清單必須採用 CSV 格式。
  • 可包含任何 UTF-8 字元。
  • 第一欄必須是檔案名稱或物件名稱。名稱是相對於根路徑或轉移工作中指定的 bucket 和資料夾;詳情請參閱「檔案系統轉移作業」和「物件儲存庫轉移作業」。
  • 資訊清單檔案不支援萬用字元。不支援沒有檔案或物件名稱的資料夾名稱。
  • 如果檔案或物件名稱包含半形逗號,則必須以雙引號括住。例如 "doe,john.txt"。
  • 資訊清單檔案的大小沒有限制。

建議您先使用一小部分檔案或物件測試移轉作業,避免因設定錯誤而產生不必要的 API 呼叫。

您可以在「Transfer Jobs」(移轉工作) 頁面監控檔案移轉狀態。移轉記錄會列出移轉失敗的檔案或物件。

檔案系統移轉作業

如要建立檔案系統上的檔案資訊清單,請建立 CSV 檔案,其中包含一欄檔案路徑,相對於建立轉移工作時指定的根目錄。

舉例來說,如要移轉下列檔案系統檔案:

檔案路徑
rootdir/dir1/subdir1/file1.txt
rootdir/file2.txt
rootdir/dir2/subdir1/file3.txt

資訊清單應如下例所示:

dir1/subdir1/file1.txt
file2.txt
dir2/subdir1/file3.txt

儲存資訊清單檔案時,請使用任意檔案名稱和 .csv 副檔名。

物件儲存空間移轉作業

如要建立物件資訊清單,請建立 CSV 檔案,並在第一欄中加入物件名稱,該名稱應相對於建立移轉工作時指定的 bucket 名稱和路徑。所有物件都必須位於同一個 bucket。

您也可以視需要指定第二欄,並填入要移轉的特定版本 Cloud Storage 產生編號。

舉例來說,您可能想轉移下列物件:

物件路徑 Cloud Storage 世代編號
SOURCE_PATH/object1.pdf 1664826685911832
SOURCE_PATH/object2.pdf
SOURCE_PATH/object3.pdf 1664826610699837

資訊清單應如下例所示:

object1.pdf,1664826685911832
object2.pdf
object3.pdf,1664826610699837

儲存資訊清單檔案時,請使用任意檔案名稱和 .csv 副檔名。

HTTP/HTTPS 移轉作業

如要從 HTTP 或 HTTPS 來源移轉特定檔案,請參閱「建立網址清單」中的操作說明。

發布資訊清單

建立資訊清單後,您必須將其提供給 Storage 移轉服務。Storage 移轉服務可以存取 Cloud Storage bucket 或檔案系統中的檔案。

將資訊清單上傳至 Cloud Storage

您可以將資訊清單檔案儲存在任何 Cloud Storage bucket。

執行移轉作業的服務代理,必須具備資訊清單所在 bucket 的 storage.objects.get 權限。如要瞭解如何找出服務代理 ID,並授予該服務代理值區權限,請參閱「授予必要權限」一節。

如需將資訊清單上傳至 bucket 的操作說明,請參閱 Cloud Storage 說明文件中的「上傳物件」。

舉例來說,如要使用 gcloud CLI 將檔案上傳至 Cloud Storage,請使用 gcloud storage cp 指令:

gcloud storage cp MANIFEST.CSV gs://DESTINATION_BUCKET_NAME/

更改下列內容:

  • MANIFEST.CSV 是資訊清單檔案的本機路徑。例如:Desktop/manifest01.csv。

  • DESTINATION_BUCKET_NAME 是要上傳物件的 bucket 名稱。例如:my-bucket。

如果成功,回應會類似以下範例:

Completed files 1/1 | 164.3kiB/164.3kiB

您可以使用客戶自行管理的 Cloud KMS 加密金鑰,加密資訊清單或包含資訊清單的值區。在這種情況下,請將用於加密資訊清單的 Cloud KMS 金鑰 Cloud KMS CryptoKey 加密者/解密者 (roles/cloudkms.cryptoKeyEncrypterDecrypter) 角色授予下列兩個服務代理:

  • Storage 移轉服務代理:project-PROJECT_NUMBER@storage-transfer-service.iam.gserviceaccount.com。
  • 專案的 Cloud Storage 服務代理,其中包含資訊清單 bucket service-PROJECT_NUMBER@gs-project-accounts.iam.gserviceaccount.com。

如要瞭解如何授予這個角色,請參閱「將 Cloud KMS 金鑰指派給服務代理」。不支援客戶提供的加密金鑰。

將資訊清單儲存在檔案系統中

您可以將資訊清單檔案儲存在來源或目的地檔案系統。

轉移代理程式必須能存取檔案位置。如果您限制代理程式的目錄存取權,請務必將資訊清單檔案放在掛接的目錄中。

開始轉移

在移轉作業完成前,請勿修改資訊清單檔案。建議您在轉移作業進行時鎖定資訊清單檔案。

Cloud 控制台

如要透過 Cloud 控制台使用資訊清單啟動移轉作業,請按照下列步驟操作:

  1. 按照「建立移轉作業」中的操作說明,選取來源、目的地和選項。

  2. 在最後一個步驟「Choose settings」(選擇設定) 中,勾選「Provide list of files to transfer via manifest file」(透過資訊清單檔案提供要轉移的檔案清單) 核取方塊。

  3. 輸入資訊清單檔案位置。

gcloud

如要移轉資訊清單中列出的檔案或物件,請在 gcloud transfer jobs create 指令中加入 --manifest-file=MANIFEST_FILE 旗標。

gcloud transfer jobs create SOURCE DESTINATION \
  --manifest-file=MANIFEST_FILE

MANIFEST_FILE 可以是下列任一值:

  • Cloud Storage bucket 中 CSV 檔案的路徑:

    --manifest-file=gs://my_bucket/sample_manifest.csv
    

    如果 bucket 或檔案並非公開,請參閱「將資訊清單上傳至 Cloud Storage」,瞭解必要權限。

  • 檔案系統 SOURCE 的相對路徑,包括指定的任何路徑:

    --manifest-file=source://relative_path/sample_manifest.csv
    
  • 檔案系統 DESTINATION 的相對路徑,包括指定的任何路徑:

    --manifest-file=destination://relative_path/sample_manifest.csv
    

REST + 用戶端程式庫

REST

如要移轉資訊清單中列出的檔案或物件,請發出 createTransferJob API 呼叫,指定 transferSpec 並新增 transferManifest 欄位。例如:

POST https://storagetransfer.googleapis.com/v1/transferJobs

...
  "transferSpec": {
      "posixDataSource": {
          "rootDirectory": "/home/",
      },
      "gcsDataSink": {
          "bucketName": "GCS_NEARLINE_SINK_NAME",
          "path": "GCS_SINK_PATH",
      },
      "transferManifest": {
          "location": "gs://my_bucket/sample_manifest.csv"
      }
  }

資訊清單檔案可以儲存在 Cloud Storage bucket,或來源/目的地檔案系統。Cloud Storage bucket 必須使用 gs:// 前置字元,並包含完整路徑 (含 bucket 名稱)。檔案系統位置必須使用 source:// 或 destination:// 前置字元,且相對於檔案系統來源或目的地,以及選用的根目錄。

Go


import (
	"context"
	"fmt"
	"io"

	storagetransfer "cloud.google.com/go/storagetransfer/apiv1"
	"cloud.google.com/go/storagetransfer/apiv1/storagetransferpb"
)

func transferUsingManifest(w io.Writer, projectID string, sourceAgentPoolName string, rootDirectory string, gcsSinkBucket string, manifestBucket string, manifestObjectName string) (*storagetransferpb.TransferJob, error) {
	// Your project id
	// projectId := "myproject-id"

	// The agent pool associated with the POSIX data source. If not provided, defaults to the default agent
	// sourceAgentPoolName := "projects/my-project/agentPools/transfer_service_default"

	// The root directory path on the source filesystem
	// rootDirectory := "/directory/to/transfer/source"

	// The ID of the GCS bucket to transfer data to
	// gcsSinkBucket := "my-sink-bucket"

	// The ID of the GCS bucket that contains the manifest file
	// manifestBucket := "my-manifest-bucket"

	// The name of the manifest file in manifestBucket that specifies which objects to transfer
	// manifestObjectName := "path/to/manifest.csv"

	ctx := context.Background()
	client, err := storagetransfer.NewClient(ctx)
	if err != nil {
		return nil, fmt.Errorf("storagetransfer.NewClient: %w", err)
	}
	defer client.Close()

	manifestLocation := "gs://" + manifestBucket + "/" + manifestObjectName
	req := &storagetransferpb.CreateTransferJobRequest{
		TransferJob: &storagetransferpb.TransferJob{
			ProjectId: projectID,
			TransferSpec: &storagetransferpb.TransferSpec{
				SourceAgentPoolName: sourceAgentPoolName,
				DataSource: &storagetransferpb.TransferSpec_PosixDataSource{
					PosixDataSource: &storagetransferpb.PosixFilesystem{RootDirectory: rootDirectory},
				},
				DataSink: &storagetransferpb.TransferSpec_GcsDataSink{
					GcsDataSink: &storagetransferpb.GcsData{BucketName: gcsSinkBucket},
				},
				TransferManifest: &storagetransferpb.TransferManifest{Location: manifestLocation},
			},
			Status: storagetransferpb.TransferJob_ENABLED,
		},
	}

	resp, err := client.CreateTransferJob(ctx, req)
	if err != nil {
		return nil, fmt.Errorf("failed to create transfer job: %w", err)
	}
	if _, err = client.RunTransferJob(ctx, &storagetransferpb.RunTransferJobRequest{
		ProjectId: projectID,
		JobName:   resp.Name,
	}); err != nil {
		return nil, fmt.Errorf("failed to run transfer job: %w", err)
	}
	fmt.Fprintf(w, "Created and ran transfer job from %v to %v using manifest file %v with name %v", rootDirectory, gcsSinkBucket, manifestLocation, resp.Name)
	return resp, nil
}

Java


import com.google.storagetransfer.v1.proto.StorageTransferServiceClient;
import com.google.storagetransfer.v1.proto.TransferProto;
import com.google.storagetransfer.v1.proto.TransferTypes.GcsData;
import com.google.storagetransfer.v1.proto.TransferTypes.PosixFilesystem;
import com.google.storagetransfer.v1.proto.TransferTypes.TransferJob;
import com.google.storagetransfer.v1.proto.TransferTypes.TransferManifest;
import com.google.storagetransfer.v1.proto.TransferTypes.TransferSpec;
import java.io.IOException;

public class TransferUsingManifest {

  public static void main(String[] args) throws IOException {
    // TODO(developer): Replace these variables before running the sample.

    // Your project id
    String projectId = "my-project-id";

    // The agent pool associated with the POSIX data source. If not provided, defaults to the
    // default agent
    String sourceAgentPoolName = "projects/my-project-id/agentPools/transfer_service_default";

    // The root directory path on the source filesystem
    String rootDirectory = "/directory/to/transfer/source";

    // The ID of the GCS bucket to transfer data to
    String gcsSinkBucket = "my-sink-bucket";

    // The ID of the GCS bucket which has your manifest file
    String manifestBucket = "my-bucket";

    // The ID of the object in manifestBucket that specifies which files to transfer
    String manifestObjectName = "path/to/manifest.csv";

    transferUsingManifest(
        projectId,
        sourceAgentPoolName,
        rootDirectory,
        gcsSinkBucket,
        manifestBucket,
        manifestObjectName);
  }

  public static void transferUsingManifest(
      String projectId,
      String sourceAgentPoolName,
      String rootDirectory,
      String gcsSinkBucket,
      String manifestBucket,
      String manifestObjectName)
      throws IOException {
    String manifestLocation = "gs://" + manifestBucket + "/" + manifestObjectName;
    TransferJob transferJob =
        TransferJob.newBuilder()
            .setProjectId(projectId)
            .setTransferSpec(
                TransferSpec.newBuilder()
                    .setSourceAgentPoolName(sourceAgentPoolName)
                    .setPosixDataSource(
                        PosixFilesystem.newBuilder().setRootDirectory(rootDirectory).build())
                    .setGcsDataSink((GcsData.newBuilder().setBucketName(gcsSinkBucket)).build())
                    .setTransferManifest(
                        TransferManifest.newBuilder().setLocation(manifestLocation).build()))
            .setStatus(TransferJob.Status.ENABLED)
            .build();

    // Initialize client that will be used to send requests. This client only needs to be created
    // once, and can be reused for multiple requests. After completing all of your requests, call
    // the "close" method on the client to safely clean up any remaining background resources,
    // or use "try-with-close" statement to do this automatically.
    try (StorageTransferServiceClient storageTransfer = StorageTransferServiceClient.create()) {

      // Create the transfer job
      TransferJob response =
          storageTransfer.createTransferJob(
              TransferProto.CreateTransferJobRequest.newBuilder()
                  .setTransferJob(transferJob)
                  .build());

      System.out.println(
          "Created and ran a transfer job from "
              + rootDirectory
              + " to "
              + gcsSinkBucket
              + " using "
              + "manifest file "
              + manifestLocation
              + " with name "
              + response.getName());
    }
  }
}

Node.js


// Imports the Google Cloud client library
const {
  StorageTransferServiceClient,
} = require('@google-cloud/storage-transfer');

/**
 * TODO(developer): Uncomment the following lines before running the sample.
 */
// Your project id
// const projectId = 'my-project'

// The agent pool associated with the POSIX data source. Defaults to the default agent
// const sourceAgentPoolName = 'projects/my-project/agentPools/transfer_service_default'

// The root directory path on the source filesystem
// const rootDirectory = '/directory/to/transfer/source'

// The ID of the GCS bucket to transfer data to
// const gcsSinkBucket = 'my-sink-bucket'

// Transfer manifest location. Must be a `gs:` URL
// const manifestLocation = 'gs://my-bucket/sample_manifest.csv'

// Creates a client
const client = new StorageTransferServiceClient();

/**
 * Creates a request to transfer from the local file system to the sink bucket
 */
async function transferViaManifest() {
  const createRequest = {
    transferJob: {
      projectId,
      transferSpec: {
        sourceAgentPoolName,
        posixDataSource: {
          rootDirectory,
        },
        gcsDataSink: {bucketName: gcsSinkBucket},
        transferManifest: {
          location: manifestLocation,
        },
      },
      status: 'ENABLED',
    },
  };

  // Runs the request and creates the job
  const [transferJob] = await client.createTransferJob(createRequest);

  const runRequest = {
    jobName: transferJob.name,
    projectId: projectId,
  };

  await client.runTransferJob(runRequest);

  console.log(
    `Created and ran a transfer job from '${rootDirectory}' to '${gcsSinkBucket}' using manifest \`${manifestLocation}\` with name ${transferJob.name}`
  );
}

transferViaManifest();

Python

from google.cloud import storage_transfer


def create_transfer_with_manifest(
    project_id: str,
    description: str,
    source_agent_pool_name: str,
    root_directory: str,
    sink_bucket: str,
    manifest_location: str,
):
    """Create a transfer from a POSIX file system to a GCS bucket using
    a manifest file."""

    client = storage_transfer.StorageTransferServiceClient()

    # The ID of the Google Cloud Platform Project that owns the job
    # project_id = 'my-project-id'

    # A useful description for your transfer job
    # description = 'My transfer job'

    # The agent pool associated with the POSIX data source.
    # Defaults to 'projects/{project_id}/agentPools/transfer_service_default'
    # source_agent_pool_name = 'projects/my-project/agentPools/my-agent'

    # The root directory path on the source filesystem
    # root_directory = '/directory/to/transfer/source'

    # Google Cloud Storage destination bucket name
    # sink_bucket = 'my-gcs-destination-bucket'

    # Transfer manifest location. Must be a `gs:` URL
    # manifest_location = 'gs://my-bucket/sample_manifest.csv'

    transfer_job_request = storage_transfer.CreateTransferJobRequest(
        {
            "transfer_job": {
                "project_id": project_id,
                "description": description,
                "status": storage_transfer.TransferJob.Status.ENABLED,
                "transfer_spec": {
                    "source_agent_pool_name": source_agent_pool_name,
                    "posix_data_source": {
                        "root_directory": root_directory,
                    },
                    "gcs_data_sink": {
                        "bucket_name": sink_bucket,
                    },
                    "transfer_manifest": {"location": manifest_location},
                },
            }
        }
    )

    result = client.create_transfer_job(transfer_job_request)
    print(f"Created transferJob: {result.name}")

資訊清單中的物件或檔案不一定會按照列出的順序移轉。

如果資訊清單包含目的地中已有的檔案,系統會略過這些檔案,除非指定「overwrite objects already existing in sink」(覆寫接收器中現有的物件) 選項。

如果資訊清單包含目的地中存在不同版本的物件,目的地中的物件會覆寫為來源版本的物件。如果目的地是已啟用版本管理功能的值區,系統會建立新版物件。

後續步驟