openplanning

Java thao tác với DigitalOcean Spaces sử dụng S3TransferManager

  1. Thư viện
  2. Tạo đối tượng S3TransferManager
  3. Upload file với S3TransferManager
  4. Download file với S3TransferManager
  5. Các ví dụ khác với S3TransferManager

1. Thư viện

<!-- https://mvnrepository.com/artifact/software.amazon.awssdk/s3 -->
<dependency>
	<groupId>software.amazon.awssdk</groupId>
	<artifactId>s3</artifactId>
	<version>2.21.10</version>
</dependency>

<!-- https://mvnrepository.com/artifact/software.amazon.awssdk/s3-transfer-manager -->
<dependency>
    <groupId>software.amazon.awssdk</groupId>
    <artifactId>s3-transfer-manager</artifactId>
    <version>2.21.10</version>
</dependency>

<!-- https://mvnrepository.com/artifact/software.amazon.awssdk.crt/aws-crt -->
<dependency>
    <groupId>software.amazon.awssdk.crt</groupId>
    <artifactId>aws-crt</artifactId>
    <version>0.28.0</version>
</dependency>

<!-- Log Library -->
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>2.0.9</version>
</dependency>

<!-- Log Library -->
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-simple -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-simple</artifactId>
    <version>2.0.9</version> 
</dependency>  

2. Tạo đối tượng S3TransferManager

Tạo đối tượng S3TransferManager để tương tác với DigitalOcean Spaces Bucket hơi khác biệt một chút so với việc tạo đối tượng S3TransferManager để tương tác với Amazon S3 Bucket.
Bạn cần phải kiểm tra xem Spaces Bucket của bạn đang được đặt tại trung tâm dữ liệu nào, từ đó biết được "SLUG" của nó.
DigitalOcean Data centers
Slug
Một chuỗi mà con người có thể đọc được, được sử dụng làm mã định danh duy nhất cho từng khu vực (Region).
Dưới đây là các phương thức tiện ích tạo đối tượng S3TransferManager cho phép bạn tương tác với Spaces Bucket.
MyS3TransferManagerUtils.java
package org.o7planning.java_14253_do_spaces.utils;

import java.net.URI;

import org.o7planning.java_14253_do_spaces.MyAccessKey;

import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.AwsCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3AsyncClient;
import software.amazon.awssdk.transfer.s3.S3TransferManager;
import software.amazon.awssdk.transfer.s3.SizeConstant;

public class MyUtils {

	// 
	// @digitalOceanSlug: nyc1, nyc2, fra1,..
	// @see: https://docs.digitalocean.com/products/platform/availability-matrix/
	//
	public static S3TransferManager createS3TransferManager(String digitalOceanSlug) {
		final Region region = Region.of(digitalOceanSlug);
		final String endpointUrl = "https://" + digitalOceanSlug + ".digitaloceanspaces.com";
		//
		AwsCredentials credentials = AwsBasicCredentials.create( //
				MyAccessKey.DO_ACCESS_KEY_ID, //
				MyAccessKey.DO_SECRET_ACCESS_KEY //
		);

		StaticCredentialsProvider credentialsProvider = StaticCredentialsProvider.create(credentials);

		S3AsyncClient s3AsyncClient = S3AsyncClient.builder() //
				.credentialsProvider(credentialsProvider) //
				.region(region) //
				.endpointOverride(URI.create(endpointUrl)) // IMPORTANT for DigitalOcean
				.build();

		return S3TransferManager.builder().s3Client(s3AsyncClient).build();
	}

	// 
	// @digitalOceanSlug: nyc1, nyc2, fra1,..
	// @see: https://docs.digitalocean.com/products/platform/availability-matrix/
	//
	// This method is currently not working (Need to wait for CRT to be supported by DigitalOcean)
	// NOTE: 2023 November, CRT still is not supported by DigitalOcean. (*****)
	// CRT: AWS Common Runtime (CRT)
	// https://aws.amazon.com/blogs/developer/introducing-crt-based-s3-client-and-the-s3-transfer-manager-in-the-aws-sdk-for-java-2-x/
	public static S3TransferManager createCrtS3TransferManager(String digitalOceanSlug) {
		final Region region = Region.of(digitalOceanSlug);
		final String endpointUrl = "https://" + digitalOceanSlug + ".digitaloceanspaces.com";
		//
		AwsCredentials credentials = AwsBasicCredentials.create( //
				MyAccessKey.DO_ACCESS_KEY_ID, //
				MyAccessKey.DO_SECRET_ACCESS_KEY //
		);

		StaticCredentialsProvider credentialsProvider = StaticCredentialsProvider.create(credentials);

		S3AsyncClient s3AsyncClient = S3AsyncClient.crtBuilder() // (*****)
				.credentialsProvider(credentialsProvider) //
				.region(region) //
				.endpointOverride(URI.create(endpointUrl)) // IMPORTANT for DigitalOcean
				.targetThroughputInGbps(20.0) //
				.minimumPartSizeInBytes(10 * SizeConstant.MB) //
				.build();

		return S3TransferManager.builder().s3Client(s3AsyncClient).build();
	}
}
Chú ý: Phương thức MyUtils.createCrtS3TransferManager chưa hoạt động với DO-Spaces tại thời điểm tôi viết bài viết này (Tháng 11-2023). Nó là một cách mới để tạo đối tượng S3TransferManager dựa trên thư viện Amazon CRT (AWS Common Runtime), nó giúp việc upload, download nhanh hơn 70%. Có lẽ cần thời gian để nó được DigitalOcean hỗ trợ.
Xem thêm các cách khác để tạo đối tượng AwsCredentialsProvider với "accessKeyId/secretAccessKey" được thiết lập tại các vị trí an toàn hơn thay vì code cứng trên mã Java. Chú ý: Bạn cũng cần một vài thay đổi nhỏ để đảm bảo nó làm việc với Spaces Bucket.

3. Upload file với S3TransferManager

Ví dụ đơn giản, upload một file lên Spaces Bucket sử dụng S3TransferManager.
S3TransferManagerUploadExample1.java
package org.o7planning.java_14253_do_spaces.example;

import java.nio.file.Paths;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletionException;

import org.o7planning.java_14253_do_spaces.utils.MyUtils;

import software.amazon.awssdk.transfer.s3.S3TransferManager;
import software.amazon.awssdk.transfer.s3.model.CompletedFileUpload;
import software.amazon.awssdk.transfer.s3.model.FileUpload;
import software.amazon.awssdk.transfer.s3.model.UploadFileRequest;
import software.amazon.awssdk.transfer.s3.progress.LoggingTransferListener;

public class S3TransferManagerUploadExample1 {
	// DigitalOcean SLUG List:
	// https://docs.digitalocean.com/products/platform/availability-matrix/

	private static final String MY_DO_SLUG = "fra1";
	private static final String MY_DO_SPACES_NAME = "my-do-spaces";

	public static void uploadFile(S3TransferManager transferManager, String bucketName, //
			String key, String filePath) {
		// Create UploadFileRequest.
		UploadFileRequest uploadFileRequest = UploadFileRequest.builder()
				.putObjectRequest(b -> b.bucket(bucketName).key(key)) // bucketName & key
				.addTransferListener(LoggingTransferListener.create()) // Listener
				.source(Paths.get(filePath)) // filePath
				.build();

		FileUpload fileUpload = transferManager.uploadFile(uploadFileRequest);
		try {
			CompletedFileUpload completedFileUpload = fileUpload //
					.completionFuture() // CompletableFuture<CompletedDirectoryUpload>
					.join(); // Wait until completed, and return value.
			//
			System.out.println("SuccessfullyCompleted");
			System.out.println(" --> " + completedFileUpload.toString());
		} catch (CancellationException e) {
			System.out.println("CancellationException");
			System.out.println(" --> (It could be by the user calling the pause method.)");
		} catch (CompletionException e) {
			System.out.println("Completed with error: " + e);
		}
	}

	public static void main(String[] args) {
		// There are several ways to create an S3TransferManager
		// @See my article 14231.
		S3TransferManager transferManager = MyUtils.createS3TransferManager(MY_DO_SLUG);
		//
		uploadFile(transferManager, //
				MY_DO_SPACES_NAME, // DigitalOcean Spaces Name.
				"static/videos/sample-15s.mp4", // key
				"/Volumes/New/Test/download/sample-15s.mp4" // filePath to save.
		);
	}
}
Output:
[main] WARN software.amazon.awssdk.transfer.s3.S3TransferManager 
- The provided DefaultS3AsyncClient is not an instance of S3CrtAsyncClient, and thus multipart upload/download feature is not enabled and resumable file upload is not supported. To benefit from maximum throughput, consider using S3AsyncClient.crtBuilder().build() instead.
[main] INFO software.amazon.awssdk.transfer.s3.progress.LoggingTransferListener - Transfer initiated...
[main] INFO software.amazon.awssdk.transfer.s3.progress.LoggingTransferListener - |                    | 0.0%
[Thread-0] INFO software.amazon.awssdk.transfer.s3.progress.LoggingTransferListener - |====================| 100.0%
SuccessfullyCompleted
 --> CompletedFileUpload(response=PutObjectResponse(ETag="044d7d8ed37e78d6537c8ab5d68b2d78"))

4. Download file với S3TransferManager

Ví dụ, download file với S3TransferManager.
S3TransferManagerDownloadExample1.java
package org.o7planning.java_14253_do_spaces.example;

import java.nio.file.Paths;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletionException;

import org.o7planning.java_14253_do_spaces.utils.MyUtils;

import software.amazon.awssdk.transfer.s3.S3TransferManager;
import software.amazon.awssdk.transfer.s3.model.CompletedFileDownload;
import software.amazon.awssdk.transfer.s3.model.DownloadFileRequest;
import software.amazon.awssdk.transfer.s3.model.FileDownload;
import software.amazon.awssdk.transfer.s3.progress.LoggingTransferListener;

public class S3TransferManagerDownloadExample1 {
	// DigitalOcean SLUG List:
	// https://docs.digitalocean.com/products/platform/availability-matrix/

	private static final String MY_DO_SLUG = "fra1";
	private static final String MY_DO_SPACES_NAME = "my-do-spaces";
 
	
	public static void downloadFile(S3TransferManager transferManager, String bucketName, //
			String key, String filePath) {
		DownloadFileRequest downloadFileRequest = DownloadFileRequest.builder()
				.getObjectRequest(req -> req.bucket(bucketName).key(key)) // bucketName & key
				.destination(Paths.get(filePath))
				// Attaching a LoggingTransferListener that will log the progress
				.addTransferListener(LoggingTransferListener.create()).build();

		FileDownload fileDownload = transferManager.downloadFile(downloadFileRequest);
		try {
			// Wait for the transfer to complete
			CompletedFileDownload completedFileDownload = fileDownload //
					.completionFuture() // CompletableFuture<CompletedFileDownload>
					.join(); // Wait until completed, and return value.
			//
			System.out.println("SuccessfullyCompleted");
			System.out.println(" --> " + completedFileDownload.toString());
		} catch (CancellationException e) {
			System.out.println("CancellationException");
			System.out.println(" --> (It could be by the user calling the pause method.)");
		} catch (CompletionException e) {
			System.out.println("Completed with error: " + e);
		}
	}

	public static void main(String[] args) {
		// There are several ways to create an S3TransferManager
		// @See my article 14231.
		S3TransferManager transferManager = MyUtils.createS3TransferManager(MY_DO_SLUG); 
		//
		// Test download a video file 11.9MB
		//
		downloadFile(transferManager, //
				MY_DO_SPACES_NAME, // bucketName
				"static/videos/sample-15s.mp4", // key
				"/Volumes/New/Test/download/sample-15s.mp4" // filePath to save.
		);
	}
}
Output:
[main] WARN software.amazon.awssdk.transfer.s3.S3TransferManager 
- The provided DefaultS3AsyncClient is not an instance of S3CrtAsyncClient, and thus multipart upload/download feature is not enabled and resumable file upload is not supported. To benefit from maximum throughput, consider using S3AsyncClient.crtBuilder().build() instead.
[main] INFO software.amazon.awssdk.transfer.s3.progress.LoggingTransferListener - Transfer initiated...
[aws-java-sdk-NettyEventLoop-1-2] INFO software.amazon.awssdk.transfer.s3.progress.LoggingTransferListener - |                    | 0.0%
[aws-java-sdk-NettyEventLoop-1-2] INFO software.amazon.awssdk.transfer.s3.progress.LoggingTransferListener - |=                   | 5.0%
[aws-java-sdk-NettyEventLoop-1-2] INFO software.amazon.awssdk.transfer.s3.progress.LoggingTransferListener - |==                  | 10.0% 
..
[aws-java-sdk-NettyEventLoop-1-2] INFO software.amazon.awssdk.transfer.s3.progress.LoggingTransferListener - |==================  | 90.0%
[aws-java-sdk-NettyEventLoop-1-2] INFO software.amazon.awssdk.transfer.s3.progress.LoggingTransferListener - |=================== | 95.0%
[aws-java-sdk-NettyEventLoop-1-2] INFO software.amazon.awssdk.transfer.s3.progress.LoggingTransferListener - |====================| 100.0%
SuccessfullyCompleted
 --> CompletedFileDownload(response=GetObjectResponse(AcceptRanges=bytes, LastModified=2023-11-04T12:38:49Z, ContentLength=11916526, ETag="aa355e698dca652d60c0bd39931714a4", ContentType=video/mp4, Metadata={}))
[sdk-async-response-0-0] INFO software.amazon.awssdk.transfer.s3.progress.LoggingTransferListener - Transfer complete!

5. Các ví dụ khác với S3TransferManager

Như đã đề cập DigitalOcean Spaces tương thích với Awssdk S3 API vì vậy các bài viết về Awssdk S3 dưới đây sẽ cung cấp cho bạn rất nhiều ví dụ khác. Chẳng hạn:
  • Liệt kê các đối tượng trên Spaces Bucket.
  • Upload một thư mục.
  • Sao chép và di chuyển các đối tượng giữa các Spaces Bucket.
  • ...
  • Các hướng dẫn Amazon Web Services

Các hướng dẫn Amazon Web Services

Show More