切片对象下载

下载大型文件的一种策略称为切片对象下载。在此类下载中,范围内的 GET 请求会并行发出,并将数据存储在临时的预分配目标文件中。所有切片都下载完毕后,临时文件会被重命名为目标文件。

如果网络和磁盘速度足够快,则切片对象下载速度明显更快。但是,切片对象下载会导致磁盘上的不同位置发生多次写入,因此此下载策略可能会降低还原时间较长的磁盘的性能,尤其是在将下载分解为大量切片时。Google Cloud CLI 等工具对于其创建的切片数具有低默认值,以最大限度地减少性能影响。

切片对象下载应始终使用快速可组合校验和 (CRC32C) 来验证切片的数据完整性。如需执行切片对象下载,gcloud CLI 等工具需要在执行下载的机器上使用编译版 crcmod。如果已编译的 crcmod 不可用,则 gcloud CLI 会改为执行非切片对象下载。

工具和 API 如何使用切片对象下载

根据您与 Cloud Storage 的交互方式,系统可能会代表您自动管理切片对象下载。此部分介绍了不同工具的切片对象下载行为,并说明如何修改该行为。

控制台

Google Cloud 控制台不会执行切片对象下载。

命令行

默认情况下,gcloud storage cp 会启用切片对象下载。 您可以通过修改以下属性来控制 gcloud CLI 执行切片对象下载的方式和时间:

  • storage/sliced_object_download_threshold:执行切片对象下载的文件总大小下限。您可以通过将此值设置为 0 来停用所有切片对象下载。

  • storage/sliced_object_download_max_components:要在下载中使用的切片数量上限。设置 0 即无限制,在这种情况下,切片数量仅由 storage/sliced_object_download_component_size 决定。

  • storage/sliced_object_download_component_size:每个下载切片的目标大小。如果文件总大小太大,导致下载此大小的切片所需要的切片数量超出允许的数量(如 storage/sliced_object_download_max_components 中设置的数量),则系统会忽略此属性。

如需修改这些属性,您可以创建命名配置并使用 --configuration 项目范围标志来按命令应用配置,也可以使用 gcloud config set 命令为所有 gcloud CLI 命令应用配置。

使用 gcloud CLI 执行切片对象下载时,系统不需要额外的本地磁盘空间。如果下载在完成之前失败,请再次运行该命令以恢复失败的切片。重试时,系统不会重新下载失败之前已成功下载的切片,除非源对象在两次下载尝试之间发生了变化。

临时下载的对象会显示在目标目录中,其名称中带有后缀 _.gstmp

客户端库

Node.js

如需了解详情,请参阅 Cloud Storage Node.js API 参考文档

如需向 Cloud Storage 进行身份验证,请设置应用默认凭据。 如需了解详情,请参阅为本地开发环境设置身份验证

您可以使用 downloadFileInChunks 方法执行切片对象下载。例如:

/**
 * TODO(developer): Uncomment the following lines before running the sample.
 */
// The ID of your GCS bucket
// const bucketName = 'your-unique-bucket-name';

// The ID of the GCS file to download
// const fileName = 'your-file-name';

// The path to which the file should be downloaded
// const destFileName = '/local/path/to/file.txt';

// The size of each chunk to be downloaded
// const chunkSize = 1024;

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

// Creates a client
const storage = new Storage();

// Creates a transfer manager client
const transferManager = new TransferManager(storage.bucket(bucketName));

async function downloadFileInChunksWithTransferManager() {
  // Downloads the files
  await transferManager.downloadFileInChunks(fileName, {
    destination: destFileName,
    chunkSizeBytes: chunkSize,
  });

  console.log(
    `gs://${bucketName}/${fileName} downloaded to ${destFileName}.`
  );
}

downloadFileInChunksWithTransferManager().catch(console.error);

Python

如需了解详情,请参阅 Cloud Storage Python API 参考文档

如需向 Cloud Storage 进行身份验证,请设置应用默认凭据。 如需了解详情,请参阅为本地开发环境设置身份验证

您可以使用 download_chunks_concurrently 方法执行切片对象下载。例如:

def download_chunks_concurrently(
    bucket_name, blob_name, filename, chunk_size=32 * 1024 * 1024, workers=8
):
    """Download a single file in chunks, concurrently in a process pool."""

    # The ID of your GCS bucket
    # bucket_name = "your-bucket-name"

    # The file to be downloaded
    # blob_name = "target-file"

    # The destination filename or path
    # filename = ""

    # The size of each chunk. The performance impact of this value depends on
    # the use case. The remote service has a minimum of 5 MiB and a maximum of
    # 5 GiB.
    # chunk_size = 32 * 1024 * 1024 (32 MiB)

    # The maximum number of processes to use for the operation. The performance
    # impact of this value depends on the use case, but smaller files usually
    # benefit from a higher number of processes. Each additional process occupies
    # some CPU and memory resources until finished. Threads can be used instead
    # of processes by passing `worker_type=transfer_manager.THREAD`.
    # workers=8

    from google.cloud.storage import Client, transfer_manager

    storage_client = Client()
    bucket = storage_client.bucket(bucket_name)
    blob = bucket.blob(blob_name)

    transfer_manager.download_chunks_concurrently(
        blob, filename, chunk_size=chunk_size, max_workers=workers
    )

    print("Downloaded {} to {}.".format(blob_name, filename))

REST API

JSON APIXML API 都支持范围内的 GET 请求,这意味着您可以使用任一 API 实现自己的切片对象下载策略。

为了防止由于源对象在下载期间发生变化而导致数据损坏,您应该在对象切片的每个下载请求中提供源对象的世代编号