1. 概述
本教程将介绍如何使用 JetS3t 库与 Amazon S3 交互。我们将完成以下核心操作:
- 创建存储桶(Bucket)
- 写入数据
- 读取数据
- 复制数据
- 列表与删除操作
2. JetS3t 环境搭建
2.1 Maven 依赖
首先在 pom.xml
中添加 JetS3t 和 Apache HttpClient 依赖:
<dependency>
<groupId>org.lucee</groupId>
<artifactId>jets3t</artifactId>
<version>0.9.4.0014L</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
✅ 最新版本可在 Maven Central 获取
✅ 源码托管在 SourceForge
测试时需要 Apache Commons Codec,额外添加:
<dependency>
<groupId>org.lucee</groupId>
<artifactId>commons-codec</artifactId>
<version>1.10.L001</version>
</dependency>
2.2 AWS 密钥配置
连接 S3 需要有效的 AWS 访问密钥:
⚠️ JetS3t 使用 Apache Commons Logging,建议统一日志框架
3. 连接存储服务
3.1 建立 AWS 连接
使用密钥创建连接:
AWSCredentials awsCredentials
= new AWSCredentials("your-access-key", "your-secret-key");
s3Service = new RestS3Service(awsCredentials);
RestS3Service
是核心连接类,通过 REST 协议与 S3 通信。
3.2 验证连接有效性
通过列出所有存储桶验证连接:
S3Bucket[] myBuckets = s3Service.listAllBuckets();
✅ 无异常抛出即表示连接成功
❌ 数组可能为空(首次使用无存储桶)
4. 存储桶管理
S3 是对象存储系统,数据以对象形式存储在存储桶中。所有 S3 存储桶共享全局命名空间,名称必须全局唯一。
4.1 创建存储桶
尝试创建名为 mybucket
的存储桶:
S3Bucket bucket = s3Service.createBucket("mybucket");
❌ 会抛出异常(名称已被占用):
org.jets3t.service.S3ServiceException: Service Error Message. -- ResponseCode: 409, ResponseStatus: Conflict <code>BucketAlreadyExists</code> The requested bucket name is not available.
改用唯一名称重试:
S3Bucket bucket = s3Service.createBucket("myuniquename");
log.info(bucket);
成功时输出:
[INFO] JetS3tClient - S3Bucket
[name=myuniquename,location=US,creationDate=Sat Mar 31 16:47:47 EDT 2018,owner=null]
4.2 删除存储桶
删除前必须清空存储桶:
s3Service.deleteBucket("myuniquename");
❌ 非空存储桶会抛出异常
4.3 指定存储桶区域
默认区域为美国弗吉尼亚北部(us-east-1
),可指定其他区域:
S3Bucket euBucket
= s3Service.createBucket("eu-bucket", S3Bucket.LOCATION_EUROPE);
S3Bucket usWestBucket = s3Service
.createBucket("us-west-bucket", S3Bucket.LOCATION_US_WEST);
S3Bucket asiaPacificBucket = s3Service
.createBucket("asia-pacific-bucket", S3Bucket.LOCATION_ASIA_PACIFIC);
✅ JetS3t 预定义了所有区域常量
5. 数据上传、下载与删除
存储桶创建后可添加对象。存储桶设计为长期持久化,无对象数量或大小限制。
5.1 字符串数据
上传字符串数据:
S3Object stringObject = new S3Object("object name", "string object");
s3Service.putObject("myuniquebucket", stringObject);
✅ 对象名称仅需在桶内唯一(非全局唯一)
✅ JetS3t 自动设置Content-Type: text/plain
获取对象元数据(不下载内容):
StorageObject objectDetailsOnly
= s3Service.getObjectDetails("myuniquebucket", "my string");
log.info("Content type: " + objectDetailsOnly.getContentType()
+ " length: " + objectDetailsOnly.getContentLength());
输出:
[INFO] JetS3tClient - Content type: text/plain; charset=utf-8 length: 9
下载并验证数据:
S3Object downloadObject =
s3Service.getObject("myuniquebucket", "string object");
String downloadString = new BufferedReader(new InputStreamReader(
object.getDataInputStream())).lines().collect(Collectors.joining("\n"));
assertTrue("string object".equals(downloadString));
5.2 文件数据
上传文件:
File file = new File("src/test/resources/test.jpg");
S3Object fileObject = new S3Object(file);
s3Service.putObject("myuniquebucket", fileObject);
✅ 对象名自动使用文件基名(如
test.jpg
)
✅ JetS3t 自动检测 MIME 类型(需mime.types
文件)
获取文件元数据:
[INFO] JetS3tClient - Content type: application/octet-stream
下载并校验文件完整性:
String getFileMD5(String filename) throws IOException {
try (FileInputStream fis = new FileInputStream(new File(filename))) {
return DigestUtils.md5Hex(fis);
}
}
S3Object fileObject = s3Service.getObject("myuniquebucket", "test.jpg");
File newFile = new File("/tmp/newtest.jpg");
Files.copy(fileObject.getDataInputStream(), newFile.toPath(),
StandardCopyOption.REPLACE_EXISTING);
String origMD5 = getFileMD5("src/test/resources/test.jpg");
String newMD5 = getFileMD5("src/test/resources/newtest.jpg");
assertTrue(origMD5.equals(newMD5));
5.3 流式数据
非字符串/文件类型需手动设置元数据:
ArrayList<Integer> numbers = new ArrayList<>();
// 添加数据到列表...
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(bytes);
objectOutputStream.writeObject(numbers);
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes.toByteArray());
S3Object streamObject = new S3Object("stream");
streamObject.setDataInputStream(byteArrayInputStream);
streamObject.setContentLength(byteArrayInputStream.available());
streamObject.setContentType("binary/octet-stream");
s3Service.putObject(BucketName, streamObject);
⚠️ 必须显式设置
Content-Type
和Content-Length
下载流数据并反序列化:
S3Object newStreamObject = s3Service.getObject(BucketName, "stream");
ObjectInputStream objectInputStream = new ObjectInputStream(
newStreamObject.getDataInputStream());
ArrayList<Integer> newNumbers = (ArrayList<Integer>) objectInputStream
.readObject();
assertEquals(2, (int) newNumbers.get(0));
assertEquals(3, (int) newNumbers.get(1));
assertEquals(5, (int) newNumbers.get(2));
assertEquals(7, (int) newNumbers.get(3));
6. 数据复制、移动与重命名
6.1 复制对象
可直接在 S3 内部复制对象(无需下载):
S3Object targetObject = new S3Object("testcopy.jpg");
s3Service.copyObject(
BucketName, "test.jpg",
"myuniquebucket", targetObject, false);
// 验证复制结果
S3Object newFileObject = s3Service.getObject(
"myuniquebucket", "testcopy.jpg");
File newFile = new File("src/test/resources/testcopy.jpg");
Files.copy(
newFileObject.getDataInputStream(),
newFile.toPath(),
REPLACE_EXISTING);
String origMD5 = getFileMD5("src/test/resources/test.jpg");
String newMD5 = getFileMD5("src/test/resources/testcopy.jpg");
assertTrue(origMD5.equals(newMD5));
✅ 支持同桶/跨桶复制
✅ 末尾参数控制是否更新元数据(false
保留源元数据)
更新元数据示例:
targetObject = new S3Object("testcopy.jpg");
targetObject.addMetadata("My_Custom_Field", "Hello, World!");
s3Service.copyObject(
"myuniquebucket", "test.jpg",
"myuniquebucket", targetObject, true);
6.2 移动对象
移动 = 复制 + 删除(仅支持同区域):
s3Service.moveObject(
"myuniquebucket",
"test.jpg",
"myotheruniquebucket",
new S3Object("spidey.jpg"),
false);
✅ 复制失败时不会删除源对象
✅ 删除失败时源/目标对象均存在
6.3 重命名对象
使用便捷方法重命名:
s3Service.renameObject(
"myuniquebucket", "test.jpg", new S3Object("spidey.jpg"));
7. 总结
本教程演示了使用 JetS3t 操作 Amazon S3 的核心流程:
- 连接配置与认证
- 存储桶的创建/删除与区域管理
- 多种数据类型(字符串/文件/流)的上传下载
- 对象的复制、移动与重命名
✅ JetS3t 提供了简洁的 API 封装,大幅简化 S3 操作
⚠️ 注意全局命名空间和区域限制等特性