亚马逊S3文件和目录API操作干货
前言:
国内云厂商也有对象存储服务,比如阿里云的OSS,但是如果你的应用面向国外用户,可能会面临一些数据国外要求不能存在中国的问题,那你就需要使用Google的cloud storage或者亚马逊的S3这类国外的对象存储服务,这篇文章用JAVA代码向读者展示S3里文件和目录的增删改查怎么做。
首先你需要在S3上先创建一个Bucket,然后集成官方的S3开发SDK,我用的是V2版接口的SDK:
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>bom</artifactId>
<version>2.16.60</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>s3</artifactId>
<version>2.16.60</version>
</dependency>
项目配置参数:
aws:
access-key: 你的API访问key,亚马逊登录账号后可查看
access-secret: 你的API访问密钥,亚马逊登录账号后可创建
bucket: 你的桶名
客户端初始化工具类:
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.S3Configuration;
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
@Data
@Component
public class AwsS3Utils {
@Value("${aws.access-key}")
private String accessKeyId;
@Value("${aws.access-secret}")
private String secretAccessKey;
@Value(("${aws.bucket}"))
private String bucket;
private static final Region region = Region.US_EAST_1;// 替换成你自己的region
public S3Client getS3Client() {
AwsBasicCredentials awsBasicCredentials = AwsBasicCredentials.create(accessKeyId, secretAccessKey);
S3Configuration s3Config = S3Configuration.builder().pathStyleAccessEnabled(true).build();
try {
S3Client s3 = S3Client.builder()
.credentialsProvider(StaticCredentialsProvider.create(awsBasicCredentials))
.region(region)
.serviceConfiguration(s3Config)
.build();
return s3;
} catch (Exception ex) {
throw new RuntimeException(ex.getMessage());
public S3Presigner getS3Presigner() {
try {
AwsCredentialsProvider awsCredentialsProvider = () -> {
AwsCredentials awsCredentials = new AwsCredentials() {
@Override
public String accessKeyId() {
return accessKeyId;
@Override
public String secretAccessKey() {
return secretAccessKey;
return awsCredentials;
S3Presigner presigner = S3Presigner.builder()
.region(region)
.credentialsProvider(awsCredentialsProvider)
.build();
return presigner;
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
文件目录创建:
public NameNormalVo addDirMsg(CreateDirDto params, String userId) {
log.info("[FileManageServiceImpl.addDirMsg] enter service. params:[{}], user_id:{}", params, userId);
NameNormalVo result = new NameNormalVo();
try {
// 要创建的目录名
String name = params.getName().trim();
// 前缀 在什么目录下创建,根目录可传空,如果是在dir1下创建dir2,那name就是dir2,prefix就是dir1
String prefix = params.getPrefix().trim() + "/";
S3Client s3Client = awsS3Utils.getS3Client();
software.amazon.awssdk.services.s3.model.PutObjectRequest putObjectRequest = PutObjectRequest.builder()
.bucket(awsS3Utils.getBucket())
.key(prefix + name + "/")
.build();
RequestBody requestBody = RequestBody.empty();
s3Client.putObject(putObjectRequest, requestBody);
result.setName(params.getName());
} catch (NorthException ex) {
log.error("[FileManageServiceImpl.addDirMsg] add dir msg failed!Error is:[{}].", ex.getMessage());
throw ex;
} catch (Exception ex) {
ex.printStackTrace();
throw new NorthException(NorthErrorEnum.SERVER_INTERNAL_ERROR);
log.info("[FileManageServiceImpl.addDirMsg] out service.");
return result;
}
效果:
修改文件名或者修改目录名 (注意,S3没有直接修改的API,修改方式是调用copy API copy出新的资源,然后再删除老的):
public NameNormalVo updateDirMsg(UpdateDirDto params, String userId) {
log.info("[FileManageServiceImpl.updateDirMsg] enter service. params:[{}], user_id:{}", params, userId);
NameNormalVo result = new NameNormalVo();
try {
String name = params.getName().trim();
String prefix = params.getPrefix() + "/";
boolean isDir = false;
if (!prefix.contains(".")) {
isDir = true;
S3Client s3Client = awsS3Utils.getS3Client();
if (!isDir) {
// 修改文件名,先复制一个文件
String newKey = prefix.substring(0, prefix.lastIndexOf("/")) + "/" + name + prefix.substring(prefix.indexOf("."));
CopyObjectRequest copyReq = CopyObjectRequest.builder()
.copySource(URLEncoder.encode(awsS3Utils.getBucket() + "/" + prefix, "UTF-8"))// 注意!这里要带上桶名,否则会报错找不到桶
.destinationBucket(awsS3Utils.getBucket())
.destinationKey(newKey)
.contentDisposition("attachment; filename=" + URLEncoder.encode(name + prefix.substring(prefix.indexOf(".")), "UTF-8"))
.metadataDirective(MetadataDirective.REPLACE)
.build();
s3Client.copyObject(copyReq);
// 对旧名称的文件进行删除
DeleteObjectRequest deleteObjectRequest = DeleteObjectRequest.builder()
.bucket(awsS3Utils.getBucket())
.key(prefix)
.build();
s3Client.deleteObject(deleteObjectRequest);
} else {
// 如果是目录,遍历目录下文件依次复制删除
ListObjectsV2Request.Builder builder = ListObjectsV2Request.builder();
// 设置bucket
builder.bucket(awsS3Utils.getBucket());
builder.prefix(prefix);
ListObjectsV2Request listObjReq = builder.build();
ListObjectsV2Response listObjRes = s3Client.listObjectsV2(listObjReq);
// 这里会得到目录下每一层级的资源路径,包括子目录和文件
List<S3Object> s3ObjectList = listObjRes.contents();
if (s3ObjectList != null && s3ObjectList.size() > 0) {
for (S3Object s3Object : s3ObjectList) {
String newPrefix = prefix.substring(0, prefix.lastIndexOf("/") + 1) + name;
String newKey = s3Object.key().replace(prefix, newPrefix);
if (!s3Object.key().contains(".")) {
// 目录下的子目录在新的复制路径下创建,相当于复制了目录
software.amazon.awssdk.services.s3.model.PutObjectRequest putObjectRequest = PutObjectRequest.builder()
.bucket(awsS3Utils.getBucket())
.key(newKey)
.build();
RequestBody requestBody = RequestBody.empty();
s3Client.putObject(putObjectRequest, requestBody);
} else {
// 目录下的文件直接复制
CopyObjectRequest copyReq = CopyObjectRequest.builder()
.copySource(URLEncoder.encode(awsS3Utils.getBucket() + "/" + s3Object.key(), "UTF-8"))
.destinationBucket(awsS3Utils.getBucket())
.destinationKey(newKey)
.contentDisposition("attachment; filename=" + URLEncoder.encode(name + newKey.substring(newKey.indexOf(".")), "UTF-8"))
.metadataDirective(MetadataDirective.REPLACE)
.build();
s3Client.copyObject(copyReq);
// 复制完删除,但是S3 API不支持指定一个文件夹路径连带文件一起删除,必须遍历文件夹下资源路径依次
//都删除完才能删除文件夹成功
for (S3Object s3Object : s3ObjectList) {
DeleteObjectRequest deleteObjectRequest = DeleteObjectRequest.builder()
.bucket(awsS3Utils.getBucket())
.key(s3Object.key())
.build();
// 复制完删除
s3Client.deleteObject(deleteObjectRequest);
result.setName(name);
} catch (NorthException ex) {
log.error("[FileManageServiceImpl.updateDirMsg] add dir msg failed!Error is:[{}].", ex.getMessage());
throw ex;
} catch (Exception ex) {
ex.printStackTrace();
throw new NorthException(NorthErrorEnum.SERVER_INTERNAL_ERROR);
log.info("[FileManageServiceImpl.updateDirMsg] out service.");
return result;
}
查询当前目录下面一级的子目录和文件:
log.info("[FileManageServiceImpl.getAwsResources] enter service.user_id:{}", userId);
SelfPageInfo<SaveFilesItemVo> result = new SelfPageInfo<>();
try {
ListObjectsV2Request.Builder builder = ListObjectsV2Request.builder();
// 设置bucket
builder.bucket(awsS3Utils.getBucket());
builder.maxKeys(1000);
// 要查询的目录前缀
builder.prefix(prefix + "/");
ListObjectsV2Request listObjReq = builder.build();
S3Client s3Client = awsS3Utils.getS3Client();
ListObjectsV2Response listObjRes = s3Client.listObjectsV2(listObjReq);
List<SaveFilesItemVo> files = new ArrayList<>();
List<S3Object> s3ObjectList = listObjRes.contents();
if (s3ObjectList != null && s3ObjectList.size() > 0) {
List<String> uniqueDirs = new ArrayList<>();
for (S3Object s3Object : s3ObjectList) {
boolean isDir = false;
String path = s3Object.key();
String fileName = path.replace(prefix, "");
if (fileName.contains("/")) {
isDir = true;
fileName = fileName.substring(0, fileName.indexOf("/"));
// 对子目录进行去重
if (!uniqueDirs.contains(fileName) && !StringUtils.isBlank(fileName)) {
SaveFilesItemVo filesItemVo = SaveFilesItemVo.builder().build();
long timeStamp = s3Object.lastModified().getEpochSecond() * 1000;
filesItemVo.setFileName(fileName);
filesItemVo.setSize(s3Object.size().intValue());
filesItemVo.setDirFlag(isDir);
filesItemVo.setTimestamp(timeStamp);
path = path.replace(userId + "/", "");
filesItemVo.setPath(path.substring(0, path.indexOf(fileName)) + fileName);
files.add(filesItemVo);
uniqueDirs.add(fileName);
} catch (NorthException ex) {
log.error("[FileManageServiceImpl.getAwsResources] query aws resources failed!Error is:[{}].", ex.getMessage());
throw ex;
} catch (Exception ex) {
ex.printStackTrace();
throw new NorthException(NorthErrorEnum.SERVER_INTERNAL_ERROR);
log.info("[FileManageServiceImpl.getAwsResources] out service.");
return result;
删除文件或者文件夹:
log.info("[FileManageServiceImpl.deleteFileResource] enter service. prefix:[{}], user_id:{}", prefix, userId);
PathNormalVo result = new PathNormalVo();
result.setPath(prefix);
try {
// 如果删除dir1下的1.png,那prefix就是dir1/1.png,如果是删除目录,那prefix就是dir1/
boolean isDir = false;
if (!prefix.contains(".")) {
isDir = true;
S3Client s3Client = awsS3Utils.getS3Client();
if (!isDir) {
// 删除文件
DeleteObjectRequest deleteObjectRequest = DeleteObjectRequest.builder()
.bucket(awsS3Utils.getBucket())
.key(prefix)
.build();
s3Client.deleteObject(deleteObjectRequest);
} else {
// 删除文件夹及下面的子目录和文件
ListObjectsV2Request.Builder builder = ListObjectsV2Request.builder();
// 设置bucket
builder.bucket(awsS3Utils.getBucket());
builder.prefix(prefix);
ListObjectsV2Request listObjReq = builder.build();
ListObjectsV2Response listObjRes = s3Client.listObjectsV2(listObjReq);
List<S3Object> s3ObjectList = listObjRes.contents();
if (s3ObjectList != null && s3ObjectList.size() > 0) {
for (S3Object s3Object : s3ObjectList) {
DeleteObjectRequest deleteObjectRequest = DeleteObjectRequest.builder()
.bucket(awsS3Utils.getBucket())
.key(s3Object.key())
.build();
// 复制完删除
s3Client.deleteObject(deleteObjectRequest);
} catch (NorthException ex) {
log.error("[FileManageServiceImpl.deleteFileResource] delete file resource msg failed!Error is:[{}].", ex.getMessage());
throw ex;
} catch (Exception ex) {