Fazendo upload de streams para o Amazon S3 usando o AWS SDK for Java 2.x - AWS SDK for Java 2.x

As traduções são geradas por tradução automática. Em caso de conflito entre o conteúdo da tradução e da versão original em inglês, a versão em inglês prevalecerá.

Fazendo upload de streams para o Amazon S3 usando o AWS SDK for Java 2.x

Quando você usa um stream para fazer upload de conteúdo para o S3 usando putObjectou uploadPart, você usa uma classe de RequestBody fábrica para a API síncrona para fornecer o stream. Para a API assíncrona, AsyncRequestBody é a classe de fábrica equivalente.

Quais métodos fazem upload de streams?

Para a API síncrona, você pode usar os seguintes métodos de fábrica RequestBody para fornecer o stream:

  • fromInputStream(InputStream inputStream, long contentLength)

    fromContentProvider(ContentStreamProvider provider, long contentLength, String mimeType)

    • ContentStreamProviderTem o método fromInputStream(InputStream inputStream) de fábrica

  • fromContentProvider(ContentStreamProvider provider, String mimeType)

Para a API assíncrona, você pode usar os seguintes métodos de fábrica de: AsyncRequestBody

  • fromInputStream(InputStream inputStream, Long contentLength, ExecutorService executor)

  • fromInputStream(AsyncRequestBodyFromInputStreamConfiguration configuration)

    • Você usa o AsyncRequestBodyFromInputStreamConfiguration .Builder para fornecer o fluxo

  • fromInputStream(Consumer<AsyncRequestBodyFromInputStreamConfiguration.Builder> configuration)

  • forBlockingInputStream(Long contentLength)

Executando o upload

Se você sabe o comprimento do riacho

Como você pode ver na assinatura dos métodos mostrados anteriormente, a maioria dos métodos aceita um parâmetro de comprimento de conteúdo.

Se você souber o tamanho do conteúdo em bytes, forneça o valor exato:

// Always provide the exact content length when it's available. long contentLength = 1024; // Exact size in bytes. s3Client.putObject(req -> req .bucket("my-bucket") .key("my-key"), RequestBody.fromInputStream(inputStream, contentLength));
Atenção

Ao fazer o upload de um stream de entrada, se o tamanho do conteúdo especificado não corresponder à contagem real de bytes, você poderá experimentar:

  • Objetos truncados se o comprimento especificado for muito pequeno

  • Falha nos uploads ou conexões interrompidas se o tamanho especificado for muito grande

Se você não sabe a duração do stream

Usando a API síncrona

Use ofromContentProvider(ContentStreamProvider provider, String mimeType):

public PutObjectResponse syncClient_stream_unknown_size(String bucketName, String key, InputStream inputStream) { S3Client s3Client = S3Client.create(); RequestBody body = RequestBody.fromContentProvider(ContentStreamProvider.fromInputStream(inputStream), "text/plain"); PutObjectResponse putObjectResponse = s3Client.putObject(b -> b.bucket(BUCKET_NAME).key(KEY_NAME), body); return putObjectResponse; }

Como o SDK armazena em buffer todo o stream na memória para calcular a duração do conteúdo, você pode ter problemas de memória com fluxos grandes. Se você precisar fazer upload de grandes streams com o cliente síncrono, considere usar a API de várias partes:

public static void uploadStreamToS3(String bucketName, String key, InputStream inputStream) { // Create S3 client S3Client s3Client = S3Client.create(); try { // Step 1: Initiate the multipart upload CreateMultipartUploadRequest createMultipartUploadRequest = CreateMultipartUploadRequest.builder() .bucket(bucketName) .key(key) .build(); CreateMultipartUploadResponse createResponse = s3Client.createMultipartUpload(createMultipartUploadRequest); String uploadId = createResponse.uploadId(); System.out.println("Started multipart upload with ID: " + uploadId); // Step 2: Upload parts List<CompletedPart> completedParts = new ArrayList<>(); int partNumber = 1; byte[] buffer = new byte[PART_SIZE]; int bytesRead; try { while ((bytesRead = readFullyOrToEnd(inputStream, buffer)) > 0) { // Create request to upload a part UploadPartRequest uploadPartRequest = UploadPartRequest.builder() .bucket(bucketName) .key(key) .uploadId(uploadId) .partNumber(partNumber) .build(); // If we didn't read a full buffer, create a properly sized byte array RequestBody requestBody; if (bytesRead < PART_SIZE) { byte[] lastPartBuffer = new byte[bytesRead]; System.arraycopy(buffer, 0, lastPartBuffer, 0, bytesRead); requestBody = RequestBody.fromBytes(lastPartBuffer); } else { requestBody = RequestBody.fromBytes(buffer); } // Upload the part and save the response's ETag UploadPartResponse uploadPartResponse = s3Client.uploadPart(uploadPartRequest, requestBody); CompletedPart part = CompletedPart.builder() .partNumber(partNumber) .eTag(uploadPartResponse.eTag()) .build(); completedParts.add(part); System.out.println("Uploaded part " + partNumber + " with size " + bytesRead + " bytes"); partNumber++; } // Step 3: Complete the multipart upload CompletedMultipartUpload completedMultipartUpload = CompletedMultipartUpload.builder() .parts(completedParts) .build(); CompleteMultipartUploadRequest completeRequest = CompleteMultipartUploadRequest.builder() .bucket(bucketName) .key(key) .uploadId(uploadId) .multipartUpload(completedMultipartUpload) .build(); CompleteMultipartUploadResponse completeResponse = s3Client.completeMultipartUpload(completeRequest); System.out.println("Multipart upload completed. Object URL: " + completeResponse.location()); } catch (Exception e) { // If an error occurs, abort the multipart upload System.err.println("Error during multipart upload: " + e.getMessage()); AbortMultipartUploadRequest abortRequest = AbortMultipartUploadRequest.builder() .bucket(bucketName) .key(key) .uploadId(uploadId) .build(); s3Client.abortMultipartUpload(abortRequest); System.err.println("Multipart upload aborted"); } finally { try { inputStream.close(); } catch (IOException e) { System.err.println("Error closing input stream: " + e.getMessage()); } } } finally { s3Client.close(); } } /** * Reads from the input stream into the buffer, attempting to fill the buffer completely * or until the end of the stream is reached. * * @param inputStream the input stream to read from * @param buffer the buffer to fill * @return the number of bytes read, or -1 if the end of the stream is reached before any bytes are read * @throws IOException if an I/O error occurs */ private static int readFullyOrToEnd(InputStream inputStream, byte[] buffer) throws IOException { int totalBytesRead = 0; int bytesRead; while (totalBytesRead < buffer.length) { bytesRead = inputStream.read(buffer, totalBytesRead, buffer.length - totalBytesRead); if (bytesRead == -1) { break; // End of stream } totalBytesRead += bytesRead; } return totalBytesRead > 0 ? totalBytesRead : -1; }
nota

Para a maioria dos casos de uso, recomendamos usar a API de cliente assíncrono para fluxos de tamanho desconhecido. Essa abordagem permite transferências paralelas e oferece uma interface de programação mais simples, porque o SDK manipula a segmentação do fluxo em partes de várias partes se o fluxo for grande.

Tanto o cliente assíncrono S3 padrão com várias partes habilitadas quanto o cliente S3 baseado em AWS CRT implementam essa abordagem. Mostraremos exemplos dessa abordagem na seção a seguir.

Usando a API assíncrona

Você pode fornecer null o contentLength argumento para o fromInputStream(InputStream inputStream, Long contentLength, ExecutorService executor)

exemplo usando o cliente AWS assíncrono baseado em CRT:
public PutObjectResponse crtClient_stream_unknown_size(String bucketName, String key, InputStream inputStream) { S3AsyncClient s3AsyncClient = S3AsyncClient.crtCreate(); ExecutorService executor = Executors.newSingleThreadExecutor(); AsyncRequestBody body = AsyncRequestBody.fromInputStream(inputStream, null, executor); // 'null' indicates that the // content length is unknown. CompletableFuture<PutObjectResponse> responseFuture = s3AsyncClient.putObject(r -> r.bucket(bucketName).key(key), body) .exceptionally(e -> { if (e != null){ logger.error(e.getMessage(), e); } return null; }); PutObjectResponse response = responseFuture.join(); // Wait for the response. executor.shutdown(); return response; }
exemplo usando o cliente assíncrono padrão com várias partes habilitadas:
public PutObjectResponse asyncClient_multipart_stream_unknown_size(String bucketName, String key, InputStream inputStream) { S3AsyncClient s3AsyncClient = S3AsyncClient.builder().multipartEnabled(true).build(); ExecutorService executor = Executors.newSingleThreadExecutor(); AsyncRequestBody body = AsyncRequestBody.fromInputStream(inputStream, null, executor); // 'null' indicates that the // content length is unknown. CompletableFuture<PutObjectResponse> responseFuture = s3AsyncClient.putObject(r -> r.bucket(bucketName).key(key), body) .exceptionally(e -> { if (e != null) { logger.error(e.getMessage(), e); } return null; }); PutObjectResponse response = responseFuture.join(); // Wait for the response. executor.shutdown(); return response; }