Uploads de várias partes da API XML

Nesta página, você aprenderá sobre os uploads de várias partes da API XML no Cloud Storage. Esse método de upload faz upload de arquivos em partes e os agrupa em um único objeto usando uma solicitação final. Os uploads de várias partes da API XML são compatíveis com os uploads de várias partes do Amazon S3.

Visão geral

Um upload de várias partes da API XML permite fazer upload de dados em várias partes e agrupá-los em um objeto final. Esse comportamento tem várias vantagens, especialmente para arquivos grandes:

  • É possível fazer upload de partes simultaneamente, reduzindo o tempo necessário para fazer o upload dos dados completos.

  • Se uma das operações de upload falhar, você só precisará fazer um novo upload de uma parte do objeto geral, em vez de reiniciar desde o início.

  • Como o tamanho total do arquivo não é especificado antecipadamente, você pode usar uploads de várias partes da API XML para uploads de streaming ou para compactar dados rapidamente durante o upload.

Um upload de várias partes da API XML tem três etapas necessárias:

  1. Inicie o upload usando uma solicitação POST, que inclui a especificação de todos os metadados que o objeto concluído deve ter. A resposta retornará um UploadId que você usa em todas as solicitações subsequentes associadas ao upload.

  2. Faça upload dos dados usando uma ou mais solicitações PUT.

  3. Conclua o upload usando uma solicitação POST. Essa solicitação substitui qualquer objeto existente no bucket com o mesmo nome.

Não há limite para o tempo que um upload de várias partes, e as partes enviadas podem permanecer inacabadas ou inativas em um bucket.

Considerações

As limitações a seguir se aplicam ao uso de uploads de várias partes da API XML:

  • Existem limites para o tamanho mínimo e máximo de uma peça e o número de partes usadas para montar o upload concluído.
  • Condições prévias não são compatíveis com as solicitações.
  • Hashes MD5 não existem para objetos enviados usando esse método.
  • Esse método de upload não é compatível com o Google Cloud console ou a Google Cloud CLI.

Ao trabalhar com uploads de várias partes da API XML, tenha em mente que:

Como as bibliotecas de cliente usam uploads de várias partes da API XML

Esta seção fornece informações sobre como fazer uploads de várias partes da API XML com bibliotecas de cliente compatíveis.

Bibliotecas de cliente

Java

Para mais informações, consulte a documentação de referência da API Cloud Storage Java.

Para se autenticar no Cloud Storage, configure o Application Default Credentials. Saiba mais em Configurar a autenticação para bibliotecas de cliente.

O exemplo a seguir inicia um upload de várias partes:


import com.google.cloud.storage.HttpStorageOptions;
import com.google.cloud.storage.MultipartUploadClient;
import com.google.cloud.storage.MultipartUploadSettings;
import com.google.cloud.storage.multipartupload.model.CreateMultipartUploadRequest;
import com.google.cloud.storage.multipartupload.model.CreateMultipartUploadResponse;
import java.util.HashMap;
import java.util.Map;

public class CreateMultipartUpload {
  public static void createMultipartUpload(String projectId, String bucketName, String objectName) {
    // The ID of your GCP project
    // String projectId = "your-project-id";

    // The ID of your GCS bucket
    // String sourceBucketName = "your-unique-bucket-name";

    // The ID of your GCS object
    // String sourceObjectName = "your-object-name";

    HttpStorageOptions storageOptions =
        HttpStorageOptions.newBuilder().setProjectId(projectId).build();
    MultipartUploadSettings mpuSettings = MultipartUploadSettings.of(storageOptions);
    MultipartUploadClient mpuClient = MultipartUploadClient.create(mpuSettings);

    System.out.println("Initiating multipart upload for " + objectName);

    Map<String, String> metadata = new HashMap<>();
    metadata.put("key1", "value1");
    String contentType = "text/plain";
    CreateMultipartUploadRequest createRequest =
        CreateMultipartUploadRequest.builder()
            .bucket(bucketName)
            .key(objectName)
            .metadata(metadata)
            .contentType(contentType)
            .build();

    CreateMultipartUploadResponse createResponse = mpuClient.createMultipartUpload(createRequest);
    String uploadId = createResponse.uploadId();
    System.out.println("Upload ID: " + uploadId);
  }
}

O exemplo a seguir faz upload de uma parte de um objeto individual:


import com.google.cloud.storage.HttpStorageOptions;
import com.google.cloud.storage.MultipartUploadClient;
import com.google.cloud.storage.MultipartUploadSettings;
import com.google.cloud.storage.RequestBody;
import com.google.cloud.storage.multipartupload.model.UploadPartRequest;
import com.google.cloud.storage.multipartupload.model.UploadPartResponse;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Random;

public class UploadPart {
  public static void uploadPart(
      String projectId, String bucketName, String objectName, String uploadId, int partNumber)
      throws IOException {
    // The ID of your GCP project
    // String projectId = "your-project-id";

    // The ID of your GCS bucket
    // String bucketName = "your-unique-bucket-name";

    // The ID of your GCS object
    // String objectName = "your-object-name";

    // The ID of the multipart upload
    // String uploadId = "your-upload-id";

    // The part number of the part being uploaded
    // int partNumber = 1;

    HttpStorageOptions storageOptions =
        HttpStorageOptions.newBuilder().setProjectId(projectId).build();
    MultipartUploadSettings mpuSettings = MultipartUploadSettings.of(storageOptions);
    MultipartUploadClient mpuClient = MultipartUploadClient.create(mpuSettings);

    // The minimum part size for a multipart upload is 5 MiB, except for the last part.
    byte[] bytes = new byte[5 * 1024 * 1024];
    new Random().nextBytes(bytes);
    RequestBody requestBody = RequestBody.of(ByteBuffer.wrap(bytes));

    System.out.println("Uploading part " + partNumber);
    UploadPartRequest uploadPartRequest =
        UploadPartRequest.builder()
            .bucket(bucketName)
            .key(objectName)
            .partNumber(partNumber)
            .uploadId(uploadId)
            .build();

    UploadPartResponse uploadPartResponse = mpuClient.uploadPart(uploadPartRequest, requestBody);

    System.out.println("Part " + partNumber + " uploaded with ETag: " + uploadPartResponse.eTag());
  }
}

O exemplo a seguir lista partes de objetos:


import com.google.cloud.storage.HttpStorageOptions;
import com.google.cloud.storage.MultipartUploadClient;
import com.google.cloud.storage.MultipartUploadSettings;
import com.google.cloud.storage.multipartupload.model.ListPartsRequest;
import com.google.cloud.storage.multipartupload.model.ListPartsResponse;
import com.google.cloud.storage.multipartupload.model.Part;

public class ListParts {
  public static void listParts(
      String projectId, String bucketName, String objectName, String uploadId) {
    // The ID of your GCP project
    // String projectId = "your-project-id";

    // The ID of your GCS bucket
    // String bucketName = "your-unique-bucket-name";

    // The ID of your GCS object
    // String objectName = "your-object-name";

    // The ID of the multipart upload
    // String uploadId = "your-upload-id";

    HttpStorageOptions storageOptions =
        HttpStorageOptions.newBuilder().setProjectId(projectId).build();
    MultipartUploadSettings mpuSettings = MultipartUploadSettings.of(storageOptions);
    MultipartUploadClient mpuClient = MultipartUploadClient.create(mpuSettings);

    System.out.println("Listing parts for upload ID: " + uploadId);

    ListPartsRequest listPartsRequest =
        ListPartsRequest.builder().bucket(bucketName).key(objectName).uploadId(uploadId).build();

    ListPartsResponse listPartsResponse = mpuClient.listParts(listPartsRequest);

    if (listPartsResponse.parts() == null || listPartsResponse.parts().isEmpty()) {
      System.out.println("No parts have been uploaded yet.");
      return;
    }

    System.out.println("Uploaded Parts:");
    for (Part part : listPartsResponse.parts()) {
      System.out.println("  - Part Number: " + part.partNumber());
      System.out.println("    ETag: " + part.eTag());
      System.out.println("    Size: " + part.size() + " bytes");
      System.out.println("    Last Modified: " + part.lastModified());
    }
  }
}

O exemplo a seguir conclui um upload de várias partes:


import com.google.cloud.storage.HttpStorageOptions;
import com.google.cloud.storage.MultipartUploadClient;
import com.google.cloud.storage.MultipartUploadSettings;
import com.google.cloud.storage.multipartupload.model.CompleteMultipartUploadRequest;
import com.google.cloud.storage.multipartupload.model.CompleteMultipartUploadResponse;
import com.google.cloud.storage.multipartupload.model.CompletedMultipartUpload;
import com.google.cloud.storage.multipartupload.model.CompletedPart;
import java.util.List;

public class CompleteMultipartUpload {
  public static void completeMultipartUpload(
      String projectId,
      String bucketName,
      String objectName,
      String uploadId,
      List<CompletedPart> completedParts) {

    // The ID of your GCP project
    // String projectId = "your-project-id";

    // The ID of your GCS bucket
    // String bucketName = "your-unique-bucket-name";

    // The ID of your GCS object
    // String objectName = "your-object-name";

    // The ID of the multipart upload
    // String uploadId = "your-upload-id";

    // The list of completed parts from the UploadPart responses.
    // List<CompletedPart> completedParts = ...;

    HttpStorageOptions storageOptions =
        HttpStorageOptions.newBuilder().setProjectId(projectId).build();
    MultipartUploadSettings mpuSettings = MultipartUploadSettings.of(storageOptions);
    MultipartUploadClient mpuClient = MultipartUploadClient.create(mpuSettings);

    System.out.println("Completing multipart upload for " + objectName);

    CompletedMultipartUpload completedMultipartUpload =
        CompletedMultipartUpload.builder().parts(completedParts).build();

    CompleteMultipartUploadRequest completeRequest =
        CompleteMultipartUploadRequest.builder()
            .bucket(bucketName)
            .key(objectName)
            .uploadId(uploadId)
            .multipartUpload(completedMultipartUpload)
            .build();

    CompleteMultipartUploadResponse completeResponse =
        mpuClient.completeMultipartUpload(completeRequest);

    System.out.println(
        "Upload complete for "
            + completeResponse.key()
            + " in bucket "
            + completeResponse.bucket());
    System.out.println("Final ETag: " + completeResponse.etag());
  }
}

Node.js

Saiba mais na documentação de referência Node.js da API Cloud Storage.

Para se autenticar no Cloud Storage, configure o Application Default Credentials. Saiba mais em Configurar a autenticação para bibliotecas de cliente.

É possível fazer uploads de várias partes da API XML usando o método uploadFileInChunks. Exemplo:

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

// The path of file to upload
// const filePath = 'path/to/your/file';

// The size of each chunk to be uploaded
// const chunkSize = 32 * 1024 * 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 uploadFileInChunksWithTransferManager() {
  // Uploads the files
  await transferManager.uploadFileInChunks(filePath, {
    chunkSizeBytes: chunkSize,
  });

  console.log(`${filePath} uploaded to ${bucketName}.`);
}

uploadFileInChunksWithTransferManager().catch(console.error);

Python

Saiba mais na documentação de referência Python da API Cloud Storage.

Para se autenticar no Cloud Storage, configure o Application Default Credentials. Saiba mais em Configurar a autenticação para bibliotecas de cliente.

É possível fazer uploads de várias partes da API XML usando o método upload_chunks_concurrently. Exemplo:

def upload_chunks_concurrently(
    bucket_name,
    source_filename,
    destination_blob_name,
    chunk_size=32 * 1024 * 1024,
    workers=8,
):
    """Upload a single file, in chunks, concurrently in a process pool."""
    # The ID of your GCS bucket
    # bucket_name = "your-bucket-name"

    # The path to your file to upload
    # source_filename = "local/path/to/file"

    # The ID of your GCS object
    # destination_blob_name = "storage-object-name"

    # 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. 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(destination_blob_name)

    transfer_manager.upload_chunks_concurrently(
        source_filename, blob, chunk_size=chunk_size, max_workers=workers
    )

    print(f"File {source_filename} uploaded to {destination_blob_name}.")


if __name__ == "__main__":
    argparse = argparse.ArgumentParser(
        description="Upload a file to GCS in chunks concurrently."
    )
    argparse.add_argument(
        "--bucket_name", help="The name of the GCS bucket to upload to."
    )
    argparse.add_argument(
        "--source_filename", help="The local path to the file to upload."
    )
    argparse.add_argument(
        "--destination_blob_name", help="The name of the object in GCS."
    )
    argparse.add_argument(
        "--chunk_size",
        type=int,
        default=32 * 1024 * 1024,
        help="The size of each chunk in bytes (default: 32 MiB). The remote\
              service has a minimum of 5 MiB and a maximum of 5 GiB",
    )
    argparse.add_argument(
        "--workers",
        type=int,
        default=8,
        help="The number of worker processes to use (default: 8).",
    )
    args = argparse.parse_args()
    upload_chunks_concurrently(
        args.bucket_name,
        args.source_filename,
        args.destination_blob_name,
        args.chunk_size,
        args.workers,
    )

A seguir