Migración del Transfer Manager de la versión 1 a la versión 2 de AWS SDK para Java
Esta guía de migración describe las principales diferencias entre Transfer Manager v1 y S3 Transfer Manager v2, incluidos cambios de constructores, asignaciones de métodos y código de ejemplo para operaciones comunes. Tras revisar estas diferencias, puede migrar correctamente el código existente de Transfer Manager para aprovechar el rendimiento mejorado y las operaciones asincrónicas de v2.
Acerca de la herramienta de migración del AWS SDK
AWS SDK para Java proporciona una herramienta de migración automatizada que puede migrar gran parte de la API de Transfer Manager v1 a v2. Sin embargo, la herramienta de migración no admite varias características de Transfer Manager v1. En estos casos, debe migrar manualmente el código de Transfer Manager siguiendo las instrucciones de este tema.
A lo largo de esta guía, el Estado de migración muestra si la herramienta de migración puede migrar automáticamente un constructor, un método o una característica:
-
Compatible: la herramienta de migración puede transformar automáticamente este código
-
No compatible: es necesario migrar el código manualmente
Incluso en el caso de elementos marcados como “compatibles”, revise los resultados de la migración y realice pruebas exhaustivas. La migración de Transfer Manager implica cambios significativos en la arquitectura, de operaciones sincrónicas a asincrónicas.
Descripción general
S3 Transfer Manager v2 introduce cambios significativos en la API de Transfer Manager. S3 Transfer Manager v2 se basa en operaciones asincrónicas y ofrece un mejor rendimiento, especialmente cuando se utiliza el cliente de Amazon S3 basado en AWS CRT.
Diferencias clave
-
Paquete:
com.amazonaws.services.s3.transfer→software.amazon.awssdk.transfer.s3 -
Nombres de clase:
TransferManager→S3TransferManager -
Dependencia del cliente: cliente de Amazon S3 sincrónico → cliente de Amazon S3 asincrónico (
S3AsyncClient) -
Arquitectura: operaciones sincrónicas → operaciones asincrónicas con
CompletableFuture -
Rendimiento: mejorado con compatibilidad con el cliente basado en AWS CRT
Cambios de alto nivel
| Aspecto | V1 | V2 |
|---|---|---|
| Dependencia de Maven | aws-java-sdk-s3 |
s3-transfer-manager |
| Paquete | com.amazonaws.services.s3.transfer |
software.amazon.awssdk.transfer.s3 |
| Clase principal | TransferManager |
S3TransferManager |
| Cliente de Amazon S3 | AmazonS3 (sync) |
S3AsyncClient (async) |
| Tipos de devolución | Operaciones de bloqueo | CompletableFuture<T> |
dependencias Maven
| V1 | V2 |
|---|---|
|
|
1 Última versión.
Migración de constructores de clientes
Constructores compatibles (migración automática)
| Constructor V1 | Equivalente V2 | Estado de migración |
|---|---|---|
new TransferManager() |
S3TransferManager.create() |
Soportado |
TransferManagerBuilder.
defaultTransferManager() |
S3TransferManager.create() |
Soportado |
TransferManagerBuilder.
standard().build() |
S3TransferManager.builder().build() |
Soportado |
new TransferManager(AWSCredentials) |
S3TransferManager.builder()
.s3Client(S3AsyncClient.builder()
.credentialsProvider(...).build())
.build() |
Soportado |
new TransferManager(
AWSCredentialsProvider) |
S3TransferManager.builder()
.s3Client(S3AsyncClient.builder()
.credentialsProvider(...).build())
.build() |
Compatible |
Constructores no compatibles (se requiere una migración manual)
| Constructor V1 | Equivalente V2 | Notas de migración |
|---|---|---|
new TransferManager(AmazonS3) |
Se requiere migración manual | Cree un S3AsyncClient por separado |
new TransferManager(AmazonS3,
ExecutorService) |
Se requiere migración manual | Cree un S3AsyncClient y configure ejecutor |
new TransferManager(AmazonS3,
ExecutorService, boolean) |
Se requiere migración manual | Parámetro shutDownThreadPools no compatible |
Ejemplos de migración manual
Código V1:
AmazonS3 s3Client = AmazonS3ClientBuilder.defaultClient();
TransferManager transferManager = new TransferManager(s3Client);
Código V2:
// Create an `S3AsyncClient` with similar configuration
S3AsyncClient s3AsyncClient = S3AsyncClient.builder()
.credentialsProvider(DefaultCredentialsProvider.create())
.build();
// Provide the configured `S3AsyncClient` to the S3 transfer manager builder.
S3TransferManager transferManager = S3TransferManager.builder()
.s3Client(s3AsyncClient)
.build();
Migración de métodos de cliente
Actualmente, la herramienta de migración admite métodos copy, download, upload, uploadDirectory, downloadDirectory, resumeDownload y resumeUpload básicos.
Métodos de transferencia principales
| Método V1 | Método V2 | Cambio de tipo de retorno | Estado de migración |
|---|---|---|---|
upload(String, String, File) |
uploadFile(UploadFileRequest) |
Upload → FileUpload |
Soportado |
upload(PutObjectRequest) |
upload(UploadRequest) |
Upload → Upload |
Soportado |
download(String, String, File) |
downloadFile(DownloadFileRequest) |
Download → FileDownload |
Soportado |
download(GetObjectRequest, File) |
downloadFile(DownloadFileRequest) |
Download → FileDownload |
Soportado |
copy(String, String, String, String) |
copy(CopyRequest) |
Copy → Copy |
Soportado |
copy(CopyObjectRequest) |
copy(CopyRequest) |
Copy → Copy |
Soportado |
uploadDirectory(String, String,
File, boolean) |
uploadDirectory(
UploadDirectoryRequest) |
MultipleFileUpload →
DirectoryUpload |
Soportado |
downloadDirectory(String, String, File) |
downloadDirectory(
DownloadDirectoryRequest) |
MultipleFileDownload →
DirectoryDownload |
Compatible |
Métodos de transferencia reanudables
| Método V1 | Método V2 | Estado de migración |
|---|---|---|
resumeUpload(PersistableUpload) |
resumeUploadFile(ResumableFileUpload) |
Soportado |
resumeDownload(PersistableDownload) |
resumeDownloadFile(ResumableFileDownload) |
Compatible |
Métricas del ciclo de vida
| Método V1 | Método V2 | Estado de migración |
|---|---|---|
shutdownNow() |
close() |
Compatible |
shutdownNow(boolean) |
Ajuste código manualmente mediante el método close() |
No es compatible |
Métodos de cliente V1 no compatibles
| Método V1 | Alternativa V2 | Notas |
|---|---|---|
abortMultipartUploads(String, Date) |
Use el cliente de Amazon S3 de bajo nivel | No es compatible |
getAmazonS3Client() |
Guarde una referencia por separado | No compatible; no hay getter en v2 |
getConfiguration() |
Guarde una referencia por separado | No compatible; no hay getter en v2 |
uploadFileList(...) |
Realice múltiples llamadas uploadFile() |
No es compatible |
Métodos copy con un parámetro TransferStateChangeListener |
Uso de TransferListener |
Consulte ejemplo de migración manual |
Métodos download con un parámetro S3ProgressListener |
Uso de TransferListener |
Consulte ejemplo de migración manual |
|
Métodos |
Consulte ejemplo de migración manual | |
Método upload con un parámetro ObjectMetadataProvider |
Establezca metadatos en la solicitud | Consulte ejemplo de migración manual |
Métodos uploadDirectory con parámetro *Provider |
Establezca etiquetas en la solicitud | Consulte ejemplo de migración manual |
Métodos copy con un parámetro TransferStateChangeListener
-
copy(CopyObjectRequest copyObjectRequest, AmazonS3 srcS3, TransferStateChangeListener stateChangeListener) -
copy(CopyObjectRequest copyObjectRequest, TransferStateChangeListener stateChangeListener)
// V1 ---------------------------------------------------------------------------------------------- // Initialize source S3 client AmazonS3 s3client = AmazonS3ClientBuilder.standard() .withRegion("us-west-2") .build(); // Initialize Transfer Manager TransferManager tm = TransferManagerBuilder.standard() .withS3Client(srcS3) .build(); CopyObjectRequest copyObjectRequest = new CopyObjectRequest( "amzn-s3-demo-source-bucket", "source-key", "amzn-s3-demo-destination-bucket", "destination-key" ); TransferStateChangeListener stateChangeListener = new TransferStateChangeListener() { @Override public void transferStateChanged(Transfer transfer, TransferState state) { //Implementation of the TransferStateChangeListener } }; Copy copy = tm.copy(copyObjectRequest, srcS3, stateChangeListener); // V2 ---------------------------------------------------------------------------------------------- S3AsyncClient s3AsyncClient = S3AsyncClient.builder() .region(Region.US_WEST_2) .build(); S3TransferManager transferManager = S3TransferManager.builder() .s3Client(s3AsyncClient) .build(); // Create transfer listener (equivalent to TransferStateChangeListener in v1) TransferListener transferListener = new TransferListener() { @Override public void transferInitiated(Context.TransferInitiated context) { //Implementation System.out.println("Transfer initiated"); } @Override public void bytesTransferred(Context.BytesTransferred context) { //Implementation System.out.println("Bytes transferred"); } @Override public void transferComplete(Context.TransferComplete context) { //Implementation System.out.println("Transfer completed!"); } @Override public void transferFailed(Context.TransferFailed context) { //Implementation System.out.println("Transfer failed"); } }; CopyRequest copyRequest = CopyRequest.builder() .copyObjectRequest(req -> req .sourceBucket("amzn-s3-demo-source-bucket") .sourceKey("source-key") .destinationBucket("amzn-s3-demo-destination-bucket") .destinationKey("destination-key") ) .addTransferListener(transferListener) // Configure the transferListener into the request .build(); Copy copy = transferManager.copy(copyRequest);
Métodos download con un parámetro S3ProgressListener
-
download(GetObjectRequest getObjectRequest, File file, S3ProgressListener progressListener) -
download(GetObjectRequest getObjectRequest, File file, S3ProgressListener progressListener, long timeoutMillis) -
download(GetObjectRequest getObjectRequest, File file, S3ProgressListener progressListener, long timeoutMillis, boolean resumeOnRetry)
// V1 ---------------------------------------------------------------------------------------------- S3ProgressListener progressListener = new S3ProgressListener() { @Override public void progressChanged(com.amazonaws.event.ProgressEvent progressEvent) { long bytes = progressEvent.getBytesTransferred(); ProgressEventType eventType = progressEvent.getEventType(); // Use bytes and eventType as needed } @Override public void onPersistableTransfer(PersistableTransfer persistableTransfer) { } }; Download download1 = tm.download(getObjectRequest, file, progressListener); Download download2 = tm.download(getObjectRequest, file, progressListener, timeoutMillis) Download download3 = tm.download(getObjectRequest, file, progressListener, timeoutMillis, true) // V2 ---------------------------------------------------------------------------------------------- TransferListener transferListener = new TransferListener() { @Override public void transferInitiated(Context.InitializedContext context) { // Equivalent to ProgressEventType.TRANSFER_STARTED_EVENT System.out.println("Transfer initiated"); } @Override public void bytesTransferred(Context.BytesTransferred context) { // Equivalent to ProgressEventType.REQUEST_BYTE_TRANSFER_EVENT long bytes = context.bytesTransferred(); System.out.println("Bytes transferred: " + bytes); } @Override public void transferComplete(Context.TransferComplete context) { // Equivalent to ProgressEventType.TRANSFER_COMPLETED_EVENT System.out.println("Transfer completed"); } @Override public void transferFailed(Context.TransferFailed context) { // Equivalent to ProgressEventType.TRANSFER_FAILED_EVENT System.out.println("Transfer failed: " + context.exception().getMessage()); } }; DownloadFileRequest downloadFileRequest = DownloadFileRequest.builder() .getObjectRequest(getObjectRequest) .destination(file.toPath()) .addTransferListener(transferListener) .build(); // For download1 FileDownload download = transferManager.downloadFile(downloadFileRequest); // For download2 CompletedFileDownload completedFileDownload = download.completionFuture() .get(timeoutMillis, TimeUnit.MILLISECONDS); // For download3, the v2 SDK does not have a direct equiavalent to the `resumeOnRetry` method of v1. // If a download is interrupted, you need to start a new download request.
Métodos downloadDirectory con 4 o más parámetros
-
downloadDirectory(String bucketName, String keyPrefix, File destinationDirectory, boolean resumeOnRetry) -
downloadDirectory(String bucketName, String keyPrefix, File destinationDirectory, boolean resumeOnRetry, KeyFilter filter) -
downloadDirectory(String bucketName, String keyPrefix, File destinationDirectory, KeyFilter filter)
// V1 ---------------------------------------------------------------------------------------------- KeyFilter filter = new KeyFilter() { @Override public boolean shouldInclude(S3ObjectSummary objectSummary) { //Filter implementation } }; MultipleFileDownload multipleFileDownload = tm.downloadDirectory(bucketName, keyPrefix, destinationDirectory, filter); // V2 ---------------------------------------------------------------------------------------------- // The v2 SDK does not have a direct equiavalent to the `resumeOnRetry` method of v1. // If a download is interrupted, you need to start a new download request. DownloadFilter filter = new DownloadFilter() { @Override public boolean test(S3Object s3Object) { // Filter implementation. } }; DownloadDirectoryRequest downloadDirectoryRequest = DownloadDirectoryRequest.builder() .bucket(bucketName) .filter(filter) .listObjectsV2RequestTransformer(builder -> builder.prefix(keyPrefix)) .destination(destinationDirectory.toPath()) .build(); DirectoryDownload directoryDownload = transferManager.downloadDirectory(downloadDirectoryRequest);
Método upload con parámetro ObjectMetadata
-
upload(String bucketName, String key, InputStream input, ObjectMetadata objectMetadata)
// V1 ----------------------------------------------------------------------------------------------ObjectMetadata metadata = new ObjectMetadata(); ObjectMetadata metadata = new ObjectMetadata(); metadata.setContentType("text/plain"); // System-defined metadata metadata.setContentLength(22L); // System-defined metadata metadata.addUserMetadata("myKey", "myValue"); // User-defined metadata PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, key, inputStream, metadata); Upload upload = transferManager.upload("amzn-s3-demo-bucket", "my-key", inputStream, metadata); // V2 ---------------------------------------------------------------------------------------------- /* When you use an InputStream to upload in V2, you should specify the content length and use `RequestBody.fromInputStream()`. If you don't provide the content length, the entire stream will be buffered in memory. If you can't determine the content length, we recommend using the CRT-based S3 client. */ Map<String, String> userMetadata = new HashMap<>(); userMetadata.put("x-amz-meta-myKey", "myValue"); PutObjectRequest putObjectRequest = PutObjectRequest.builder() .bucket("amzn-s3-demo-bucket1") .key("k") .contentType("text/plain") //System-defined metadata usually has separate methods in the builder. .contentLength(22L) .metadata(userMetadata) //metadata() is only for user-defined metadata. .build(); UploadRequest uploadRequest = UploadRequest.builder() .putObjectRequest(putObjectRequest) .requestBody(AsyncRequestBody.fromInputStream(stream, 22L, executor)) .build(); transferManager.upload(uploadRequest).completionFuture().join();
uploadDirectory con parámetro ObjectMetadataProvider
-
uploadDirectory(String bucketName, String virtualDirectoryKeyPrefix, File directory, boolean includeSubdirectories, ObjectMetadataProvider metadataProvider) -
uploadDirectory(String bucketName, String virtualDirectoryKeyPrefix, File directory, boolean includeSubdirectories, ObjectMetadataProvider metadataProvider, ObjectTaggingProvider taggingProvider) -
uploadDirectory(String bucketName, String virtualDirectoryKeyPrefix, File directory, boolean includeSubdirectories, ObjectMetadataProvider metadataProvider, ObjectTaggingProvider taggingProvider, ObjectCannedAclProvider cannedAclProvider)
// V1 ---------------------------------------------------------------------------------------------- tm.uploadDirectory(bucketName, virtualDirectoryKeyPrefix, directory, includeSubdirectories, metadataProvider) tm.uploadDirectory(bucketName, virtualDirectoryKeyPrefix, directory, includeSubdirectories, metadataProvider, taggingProvider) tm.uploadDirectory(bucketName, virtualDirectoryKeyPrefix, directory, includeSubdirectories, metadataProvider, taggingProvider, cannedAclProvider) // V2 ---------------------------------------------------------------------------------------------- UploadDirectoryRequest request = UploadDirectoryRequest.builder() .bucket(bucketName) .s3Prefix(virtualDirectoryKeyPrefix) .source(directory.toPath()) .maxDepth(includeSubdirectories ? Integer.MAX_VALUE : 1) .uploadFileRequestTransformer(builder -> { // 1.Replace `ObjectMetadataProvider`, `ObjectTaggingProvider`, and `ObjectCannedAclProvider` with an // `UploadFileRequestTransformer` that can combine the functionality of all three *Provider implementations. // 2. Convert your v1 `ObjectMetadata` to v2 `PutObjectRequest` parameters. // 3. Convert your v1 `ObjectTagging` to v2 `Tagging`. // 4. Convert your v1 `CannedAccessControlList` to v2 `ObjectCannedACL`. }) .build(); DirectoryUpload directoryUpload = transferManager.uploadDirectory(request);
Migración de objetos del modelo
En AWS SDK for Java 2.x, muchos de los objetos del modelo TransferManager se han rediseñado y varios métodos getter y setter disponibles en los objetos del modelo de v1 ya no son compatibles.
En v2, puede usar la clase CompletableFuture<T> para realizar acciones cuando se complete la transferencia, ya sea correctamente o con una excepción. Si es necesario, puede usar el método join() para esperar a que se complete.
Objetos de transferencia principales
| Clase V1 | Clase V2 | Estado de migración |
|---|---|---|
TransferManager |
S3TransferManager |
Soportado |
TransferManagerBuilder |
S3TransferManager.Builder |
Soportado |
Transfer |
Transfer |
Compatible |
AbortableTransfer |
Transfer |
Compatible (sin clase aparte) |
Copy |
Copy |
Soportado |
Download |
FileDownload |
Soportado |
Upload |
Upload / FileUpload |
Soportado |
MultipleFileDownload |
DirectoryDownload |
Soportado |
MultipleFileUpload |
DirectoryUpload |
Compatible |
Objetos de persistencia
| Clase V1 | Clase V2 | Estado de migración |
|---|---|---|
PersistableDownload |
ResumableFileDownload |
Soportado |
PersistableUpload |
ResumableFileUpload |
Soportado |
PersistableTransfer |
ResumableTransfer |
Compatible |
PauseResult<T> |
Objeto reanudable directo | No es compatible |
Objetos resultantes
| Clase V1 | Clase V2 | Estado de migración |
|---|---|---|
CopyResult |
CompletedCopy |
Soportado |
UploadResult |
CompletedUpload |
Compatible |
Objetos de configuración
| Clase V1 | Clase V2 | Estado de migración |
|---|---|---|
TransferManagerConfiguration |
MultipartConfiguration (en cliente de Amazon S3) |
Soportado |
TransferProgress |
TransferProgress + TransferProgressSnapshot |
Soportado |
KeyFilter |
DownloadFilter |
Compatible |
Objetos no admitidos
| Clase V1 | Alternativa V2 | Estado de migración |
|---|---|---|
PauseStatus |
No admitido | No es compatible |
UploadContext |
No admitido | No es compatible |
ObjectCannedAclProvider |
PutObjectRequest.builder().acl() |
No es compatible |
ObjectMetadataProvider |
PutObjectRequest.builder().metadata() |
No es compatible |
ObjectTaggingProvider |
PutObjectRequest.builder().tagging() |
No es compatible |
PresignedUrlDownload |
No admitido | No es compatible |
Migración de la configuración de TransferManagerBuilder
Cambios de configuración
Los cambios de configuración que debe establecer para Transfer Manager v2 dependen del cliente de S3 que utilice. Puede elegir entre el cliente de S3 basado en AWS CRT o el cliente asincrónico de S3 estándar basado en Java. Para obtener más información acerca de las diferencias, consulte el tema Clientes de S3 en AWS SDK for Java 2.x.
Cambios de comportamiento
Operaciones asíncronas
V1 (bloqueo):
Upload upload = transferManager.upload("amzn-s3-demo-bucket", "key", file);
upload.waitForCompletion(); // Blocks until complete
V2 (asincrónico):
FileUpload upload = transferManager.uploadFile(UploadFileRequest.builder()
.putObjectRequest(PutObjectRequest.builder()
.bucket("amzn-s3-demo-bucket")
.key("key")
.build())
.source(file)
.build());
CompletedFileUpload result = upload.completionFuture().join(); // Blocks until complete
// Or handle asynchronously:
upload.completionFuture().thenAccept(result -> {
System.out.println("Upload completed: " + result.response().eTag());
});
Gestión de errores
V1: las transferencias de directorio fallan por completo si se produce un error en alguna solicitud secundaria.
V2: las transferencias de directorio se completan correctamente incluso aunque fallen algunas solicitudes secundarias. Compruebe si hay errores de forma explícita:
DirectoryUpload directoryUpload = transferManager.uploadDirectory(request);
CompletedDirectoryUpload result = directoryUpload.completionFuture().join();
// Check for failed transfers
if (!result.failedTransfers().isEmpty()) {
System.out.println("Some uploads failed:");
result.failedTransfers().forEach(failed ->
System.out.println("Failed: " + failed.exception().getMessage()));
}
Descarga paralela mediante búsquedas por rango de bytes
Cuando la característica de transferencia paralela automática está habilitada en el SDK v2, Transfer Manager de S3 utiliza recuperaciones de rango de byte para recuperar partes específicas del objeto en paralelo (descarga de multiparte). La forma en que se descarga un objeto con la versión 2 no depende de cómo se cargó originalmente. Todas las descargas pueden beneficiarse de un alto rendimiento y simultaneidad.
Por el contrario, con Transfer Manager v1 es importante cómo se cargó originalmente el objeto. Transfer Manager v1 recupera las partes del objeto de la misma forma en que se cargaron las partes. Si un objeto se cargó originalmente como un objeto único, Transfer Manager v1 no puede acelerar el proceso de descarga mediante solicitudes secundarias.