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

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

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

  • 從 AWS S3、與 S3 相容的儲存空間、Azure Blob 儲存體或 Cloud Storage 到 Cloud Storage 值區。

  • 從檔案系統傳輸至 Cloud Storage 值區。

  • 從 Cloud Storage 值區到檔案系統。

  • 兩個檔案系統之間。

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

建立資訊清單

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

  • 資訊清單必須採用 CSV 格式。
  • 可包含任何 UTF-8 字元。
  • 第一個欄必須是檔案名稱或物件名稱。名稱會相對於根路徑或移轉工作中指定的值區和資料夾;詳情請參閱「檔案系統移轉」和「物件儲存空間移轉」。
  • 資訊清單檔案不支援萬用字元。不支援沒有檔案或物件名稱的資料夾名稱。
  • 如果檔案或物件名稱含有半形逗號,則必須以雙引號括住。例如 "doe,john.txt"
  • 對於使用轉移代理程式的轉移作業 (也就是檔案系統轉移或從相容於 S3 的儲存空間進行轉移),資訊清單檔案大小上限為 1 GiB,大約相當於 100 萬列。如果清單檔案大於 1 GiB,您可以將其分割成多個檔案,並執行多個轉移作業。對於無代理程式轉移作業,資訊清單檔案大小沒有限制。

建議您使用少量檔案或物件測試轉移作業,避免因設定錯誤而造成不必要的 API 呼叫。

您可以前往「轉移工作」頁面,監控檔案轉移作業的狀態。轉移記錄會列出無法轉移的檔案或物件。

檔案系統傳輸

如要在檔案系統上建立檔案資訊清單,請建立 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 檔案,第一欄應包含物件名稱,相對於建立轉移工作時指定的值區名稱和路徑。所有物件都必須位於同一個值區。

您也可以指定第二個可選資料欄,其中包含要轉移的特定版本的 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 移轉服務可以存取 Cloud Storage 值區或檔案系統中的檔案。

將資訊清單上傳至 Cloud Storage

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

執行轉移作業的服務代理程式必須具備包含資訊清單的值區的 storage.objects.get 權限。如要瞭解如何找出服務代理 ID,以及在值區中授予該服務代理權限,請參閱「授予必要權限」一文。

如要瞭解如何將資訊清單上傳至值區,請參閱 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 是您要上傳物件的值區名稱。例如:my-bucket

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

Completed files 1/1 | 164.3kiB/164.3kiB

您可以使用客戶管理的 Cloud KMS 加密金鑰加密資訊清單。在這種情況下,請務必為存取資訊清單的所有服務帳戶指派適用的加密金鑰。系統不支援客戶提供的金鑰。

將清單儲存在檔案系統中

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

檔案位置必須可供傳輸代理程式存取。如果您為代理程限制目錄存取權,請確認資訊清單檔案位於已掛載的目錄中。

開始轉移

傳輸作業完成前,請勿修改資訊清單檔案。建議您在傳輸期間鎖定清單檔案。

Cloud 控制台

如要透過 Cloud 控制台的清單開始轉移作業,請按照下列步驟操作:

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

  2. 在最後一個步驟「選擇設定」中,勾選「透過資訊清單檔案提供要轉移的檔案清單」核取方塊。

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

gcloud

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

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

MANIFEST_FILE 可以是下列任一值:

  • Cloud Storage 值區中 CSV 檔案的路徑:

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

    如果值區或檔案不是公開,請參閱將資訊清單上傳至 Cloud Storage 一文,瞭解所需權限的詳細資訊。

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

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

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

REST + 用戶端程式庫

REST

如要傳輸資訊清單中列出的檔案或物件,請發出 createTransferJob API 呼叫,指定已加入 transferManifest 欄位的 transferSpec。例如:

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 值區,或來源或目的地檔案系統中。Cloud Storage 值區必須使用 gs:// 前置字串,並包含完整路徑,包括值區名稱。檔案系統位置必須使用 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」選項,否則系統會略過這些檔案。

如果資訊清單包含在目的地不同版本中存在的物件,系統會將來源版本的物件覆寫目的地中的物件。如果目的地是版本化值區,系統會建立物件的新版本。

後續步驟