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

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

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

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

  • 從檔案系統到 Cloud Storage bucket。

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

  • 兩個檔案系統之間。

  • 從可公開存取的 HTTP/HTTPS 來源移至 Cloud Storage bucket。請按照「建立網址清單」一文中的操作說明進行,因為網址清單的資訊清單格式獨一無二。

建立資訊清單

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

  • 資訊清單必須採用 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 Transfer Service。Storage 移轉服務可以存取 Cloud Storage 值區或檔案系統中的檔案。

將資訊清單上傳至 Cloud Storage

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

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

如需將資訊清單上傳至 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 是您要向其上傳物件的值區名稱。例如:my-bucket

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

Completed files 1/1 | 164.3kiB/164.3kiB

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

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

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

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

開始轉移

請勿修改資訊清單檔案,直到轉移作業完成為止。 建議您在轉移期間鎖定資訊清單檔案。

Cloud 控制台

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

  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 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 呼叫,並指定已新增 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 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}")

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

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

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

後續步驟