Hochladen von Streams auf Amazon S3 mit dem AWS SDK for Java 2.x - AWS SDK for Java 2.x

Die vorliegende Übersetzung wurde maschinell erstellt. Im Falle eines Konflikts oder eines Widerspruchs zwischen dieser übersetzten Fassung und der englischen Fassung (einschließlich infolge von Verzögerungen bei der Übersetzung) ist die englische Fassung maßgeblich.

Hochladen von Streams auf Amazon S3 mit dem AWS SDK for Java 2.x

Wenn Sie einen Stream verwenden, um Inhalte mithilfe von putObjectoder auf S3 hochzuladen uploadPart, verwenden Sie eine RequestBody Factory-Klasse für die synchrone API, um den Stream bereitzustellen. Für die asynchrone API AsyncRequestBody ist das die entsprechende Factory-Klasse.

Welche Methoden laden Streams hoch?

Für die synchrone API können Sie die folgenden Factory-Methoden verwendenRequestBody, um den Stream bereitzustellen:

  • fromInputStream(InputStream inputStream, long contentLength)

    fromContentProvider(ContentStreamProvider provider, long contentLength, String mimeType)

    • Das ContentStreamProvider hat die fromInputStream(InputStream inputStream) Factory-Methode

  • fromContentProvider(ContentStreamProvider provider, String mimeType)

Für die asynchrone API können Sie die folgenden Factory-Methoden verwenden: AsyncRequestBody

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

  • fromInputStream(AsyncRequestBodyFromInputStreamConfiguration configuration)

    • Sie verwenden den AsyncRequestBodyFromInputStreamConfiguration .Builder, um den Stream bereitzustellen

  • fromInputStream(Consumer<AsyncRequestBodyFromInputStreamConfiguration.Builder> configuration)

  • forBlockingInputStream(Long contentLength)

Der Upload wird durchgeführt

Wenn du die Länge des Streams kennst

Wie Sie der Signatur der zuvor gezeigten Methoden entnehmen können, akzeptieren die meisten Methoden einen Parameter für die Inhaltslänge.

Wenn Sie die Inhaltslänge in Byte kennen, geben Sie den genauen Wert an:

// 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));
Warnung

Wenn Sie aus einem Eingabestream hochladen und die angegebene Inhaltslänge nicht der tatsächlichen Byteanzahl entspricht, kann Folgendes auftreten:

  • Verkürzte Objekte, wenn die angegebene Länge zu klein ist

  • Fehlgeschlagene Uploads oder hängende Verbindungen, wenn die angegebene Länge zu groß ist

Wenn Sie die Länge des Streams nicht kennen

Verwenden der synchronen API

Verwenden Sie dasfromContentProvider(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; }

Da das SDK den gesamten Stream im Speicher puffert, um die Inhaltslänge zu berechnen, kann es bei großen Streams zu Speicherproblemen kommen. Wenn Sie große Streams mit dem synchronen Client hochladen müssen, sollten Sie die Verwendung der mehrteiligen API in Betracht ziehen:

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; }
Anmerkung

Für die meisten Anwendungsfälle empfehlen wir die Verwendung der asynchronen Client-API für Streams unbekannter Größe. Dieser Ansatz ermöglicht parallel Übertragungen und bietet eine einfachere Programmierschnittstelle, da das SDK die Stream-Segmentierung in mehrteilige Blöcke übernimmt, wenn der Stream groß ist.

Sowohl der standardmäßige asynchrone S3-Client mit aktiviertem Multipart als auch der AWS CRT-basierte S3-Client implementieren diesen Ansatz. Im folgenden Abschnitt zeigen wir Beispiele für diesen Ansatz.

Verwendung der asynchronen API

Sie können das contentLength Argument null für das angeben fromInputStream(InputStream inputStream, Long contentLength, ExecutorService executor)

Beispiel mit dem AWS CRT-basierten asynchronen Client:
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; }
Beispiel unter Verwendung des standardmäßigen asynchronen Clients mit aktiviertem Multipart:
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; }