在 Java 生态中,上传文件到 S3 存储桶有多种成熟方案。本文将重点介绍 jclouds 库 提供的解决方案,涵盖四种不同层级的 API 实现。
要使用本文讨论的 jclouds API,只需在项目中添加以下 Maven 依赖:
<dependency>
<groupId>org.jclouds</groupId>
<artifactId>jclouds-allblobstore</artifactId>
<version>1.5.10</version>
</dependency>
1. 上传到 Amazon S3
使用 jclouds 的第一步是创建认证上下文。基础实现如下:
BlobStoreContext context =
ContextBuilder.newBuilder("aws-s3").credentials(identity, credentials)
.buildView(BlobStoreContext.class);
这个上下文是访问通用键值存储服务(如 Amazon S3)的入口点,但不仅限于 S3。
如果需要 S3 专用实现,可以这样创建:
BlobStoreContext context =
ContextBuilder.newBuilder("aws-s3").credentials(identity, credentials)
.buildView(S3BlobStoreContext.class);
更具体的 AWS S3 实现:
BlobStoreContext context =
ContextBuilder.newBuilder("aws-s3").credentials(identity, credentials)
.buildView(AWSS3BlobStoreContext.class);
⚠️ 重要提示:认证上下文使用完毕后必须关闭,否则会泄漏连接和线程资源。
2. jclouds 的四种 S3 API
jclouds 提供四种不同层级的上传 API,从简单到强大依次递进。所有 API 都通过 BlobStoreContext
获取。
2.1 通过 Map API 上传
最简单的 API 是将 S3 存储桶抽象为 Map。获取方式如下:
InputStreamMap bucket = context.createInputStreamMap("bucketName");
上传 HTML 文件示例:
bucket.putString("index1.html", "<html><body>hello world1</body></html>");
InputStreamMap
还支持文件、字节数组等多种上传方式,包括批量操作。
典型集成测试示例:
@Test
public void whenFileIsUploadedToS3WithMapApi_thenNoExceptions() {
BlobStoreContext context =
ContextBuilder.newBuilder("aws-s3").credentials(identity, credentials)
.buildView(AWSS3BlobStoreContext.class);
InputStreamMap bucket = context.createInputStreamMap("bucketName");
bucket.putString("index1.html", "<html><body>hello world1</body></html>");
context.close();
}
2.2 通过 BlobMap 上传
Map API 虽然简单但功能有限(无法设置元数据)。当需要更精细控制时,使用 BlobMap API:
BlobMap bucket = context.createBlobMap("bucketName");
该 API 允许设置底层细节:
- Content-Length
- Content-Type
- Content-Encoding
- eTag 哈希值等
上传文件示例:
Blob blob = bucket.blobBuilder().name("index2.html").
payload("<html><body>hello world2</body></html>").
contentType("text/html").calculateMD5().build();
集成测试示例:
@Test
public void whenFileIsUploadedToS3WithBlobMap_thenNoExceptions() throws IOException {
BlobStoreContext context =
ContextBuilder.newBuilder("aws-s3").credentials(identity, credentials)
.buildView(AWSS3BlobStoreContext.class);
BlobMap bucket = context.createBlobMap("bucketName");
Blob blob = bucket.blobBuilder().name("index2.html").
payload("<html><body>hello world2</body></html>").
contentType("text/html").calculateMD5().build();
bucket.put(blob.getMetadata().getName(), blob);
context.close();
}
2.3 通过 BlobStore 上传
前两种 API 不支持分块上传,不适合大文件场景。BlobStore API 解决了这个问题:
BlobStore blobStore = context.getBlobStore();
启用分块上传示例:
Blob blob = blobStore.blobBuilder("index3.html").
payload("<html><body>hello world3</body></html>").contentType("text/html").build();
blobStore.putBlob("bucketName", blob, PutOptions.Builder.multipart());
关键点:
- 使用与 BlobMap 相同的 payload 构建器
- 通过
PutOptions
启用分块上传功能
集成测试示例:
@Test
public void whenFileIsUploadedToS3WithBlobStore_thenNoExceptions() {
BlobStoreContext context =
ContextBuilder.newBuilder("aws-s3").credentials(identity, credentials)
.buildView(AWSS3BlobStoreContext.class);
BlobStore blobStore = context.getBlobStore();
Blob blob = blobStore.blobBuilder("index3.html").
payload("<html><body>hello world3</body></html>").contentType("text/html").build();
blobStore.putBlob("bucketName", blob, PutOptions.Builder.multipart());
context.close();
}
2.4 通过 AsyncBlobStore 上传
对于异步场景,jclouds 提供了 AsyncBlobStore API:
AsyncBlobStore blobStore = context.getAsyncBlobStore();
与同步版本的核心区别在于:
- 返回
ListenableFuture
对象 - 需要调用
.get()
获取最终结果
异步上传示例:
Blob blob = blobStore.blobBuilder("index4.html").
payload("<html><body>hello world4</body></html>").build();
blobStore.putBlob("bucketName", blob).get();
集成测试示例:
@Test
public void whenFileIsUploadedToS3WithBlobStore_thenNoExceptions() {
BlobStoreContext context =
ContextBuilder.newBuilder("aws-s3").credentials(identity, credentials)
.buildView(AWSS3BlobStoreContext.class);
BlobStore blobStore = context.getBlobStore();
Blob blob = blobStore.blobBuilder("index4.html").
payload("<html><body>hello world4</body></html>").contentType("text/html").build();
Future<String> putOp = blobStore.putBlob("bucketName", blob, PutOptions.Builder.multipart());
putOp.get();
context.close();
}
3. 总结
我们分析了 jclouds 提供的四种 S3 上传 API:
- ✅ Map API:最简单但功能有限
- ✅ BlobMap:支持元数据设置
- ✅ BlobStore:支持分块上传(大文件必备)
- ✅ AsyncBlobStore:异步非阻塞操作
这些 API 是通用设计,同样适用于其他键值存储服务(如 Microsoft Azure Storage)。
下一篇我们将深入探讨 jclouds 的 AWS 专用 API AWSS3Client
,实现:
- 大文件动态分块优化
- 并行上传所有分块
- 智能计算最优分块数量
踩坑提醒:实际使用时务必记得关闭 BlobStoreContext
,否则会导致资源泄漏!