1、引入pom
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>s3</artifactId>
<version>2.16.60</version>
</dependency>
2、yml添加aws配置信息
aws:
accessKeyId: "8MIPOASDIOI9J45J"
secretAccessKey: "dzWkljdfhjkne87+Jlkejerk7ujkjsU"
endpointUrl: "https://s3.demo.com:8088"
3、增加aws初始化配置类
@Configuration
public class AwsS3Config {
@Value("aws.accessKeyId")
private String accessKeyId;
@Value("aws.secretAccessKey")
private String secretAccessKey;
@Value("aws.endpointUrl")
private String endpointUrl;
private static final Region region = Region.US_EAST_1;
@Bean
public S3Client s3Client(){
AwsBasicCredentials awsBasicCredentials = AwsBasicCredentials.create(accessKeyId, secretAccessKey);
S3Configuration s3Config = S3Configuration.builder().pathStyleAccessEnabled(true).build();
try{
S3Client s3 = S3Client.builder()
.endpointOverride(new URI(endpointUrl))
.credentialsProvider(StaticCredentialsProvider.create(awsBasicCredentials))
.region(region)
.serviceConfiguration(s3Config)
.build();
return s3;
} catch (URISyntaxException e){
throw new RuntimeException(e.getMessage());
}
}
@Bean
public S3Presigner s3Presigner(){
try{
S3Configuration s3Config = S3Configuration.builder().pathStyleAccessEnabled(true).build();
S3Presigner presigner = S3Presigner.builder()
.endpointOverride(new URI(endpointUrl))
.region(region)
.build();
return presigner;
} catch (URISyntaxException e){
throw new RuntimeException(e.getMessage());
}
}
}
3、实现对象列表、异步对象上传、对象下载、对象删除
@Resource
private S3Client s3Client;
@Resource
private S3Presigner s3Presigner;
/**
* 对象列表
* @param bucket bucket
* @param key 对象路径
* @param pageToken 下一页token
* @param pageSize 分页大小
*/
public ResultBean list(String bucket, String key, String pageToken, Integer pageSize) throws UnsupportedEncodingException {
if(StringUtil.isEmpty(bucket)) return ResultUtil.resultFail("参数错误");
ListObjectsV2Request.Builder builder = ListObjectsV2Request.builder();
// 设置bucket
builder.bucket(bucket);
// 设置一次请求返回多少数据
builder.maxKeys(pageSize);
if(StringUtil.isNotEmpty(key)) {
// 设置文件路径分隔符,用于查找
builder.prefix(key).delimiter("/");
}
ListObjectsV2Request listObjReq = builder.build();
ListObjectsV2Response listObjRes = s3Client.listObjectsV2(listObjReq);
// 获取下一页数据
if(listObjRes.isTruncated() && StringUtil.isNotEmpty(pageToken)){
listObjReq = listObjReq.toBuilder().continuationToken(pageToken).build();
listObjRes = s3Client.listObjectsV2(listObjReq);
}
List<S3Object> s3ObjectList = listObjRes.contents();
// 获取下一页token
String pageNextToken = listObjRes.nextContinuationToken();
String finalPageNextToken = StringUtil.isEmpty(pageNextToken) ? "" : URLEncoder.encode(pageNextToken, "utf-8");
// 重新组装为自己需要的数据格式
List<AwsS3ObjectVO> s3ObjList = Lists.transform(s3ObjectList, (s3Object) -> {
AwsS3ObjectVO vo = new AwsS3ObjectVO();
vo.setBucket(bucket);
vo.setKey(s3Object.key());
vo.setSize(FileUtil.calSize(s3Object.size()));
vo.setStorageClass(s3Object.storageClassAsString());
vo.setLastModified(DateUtil.dataToStr(s3Object.lastModified(), "yyyy-MM-dd HH:mm:ss"));
vo.setPageToken(finalPageNextToken);
return vo;
});
return ResultUtil.resultSuccess(s3ObjList);
}
/**
* 对象上传
* @param bucket bucket
* @param file 文件对象
*/
public ResultBean upload(String bucket, MultipartFile file){
if(StringUtil.isEmpty(bucket) || file.isEmpty()) return ResultUtil.resultFail("参数错误");
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
String fileName = UUID.randomUUID().toString().trim().replaceAll("-", "");
String filePath = sdf.format(new Date()) + "/" + fileName;
try{
ResultBean resultBean = singleUpload(bucket, filePath, file);
if(resultBean.getCode().equals("-1")) return ResultUtil.resultFail(resultBean.getMsg());
return ResultUtil.resultSuccess(filePath);
} catch (Exception ex){
throw new BizException(ex.getMessage(), ex);
}
}
/**
* 异步完整上传不分片
* @param bucket bucket
* @param key 对象路径
* @param file 文件对象
*/
@Async("awsThreadPoolExecutor")
ResultBean singleUpload(String bucket, String key, MultipartFile file) throws IOException {
Long startTime = System.currentTimeMillis() / 1000;
PutObjectRequest putObjectRequest = PutObjectRequest.builder()
.bucket(bucket)
.key(key)
.build();
RequestBody requestBody = RequestBody.fromInputStream(file.getInputStream(), file.getSize());
PutObjectResponse putObjectResponse = s3Client.putObject(putObjectRequest, requestBody);
SdkHttpResponse sdkHttpResponse = putObjectResponse.sdkHttpResponse();
if(!sdkHttpResponse.isSuccessful()){
return ResultUtil.resultFail("上传对象存储失败, statusCode:" + sdkHttpResponse.statusCode() + "statusText:" + sdkHttpResponse.statusText());
}
long endTime = System.currentTimeMillis() / 1000;
log.info("上传文件(" + key + ")总计耗费时间为:" + (endTime - startTime) + " 秒");
return ResultUtil.resultSuccess();
}
/**
* 对象下载,返回url下载地址
* @param bucket bucket
* @param key 对象路径
*/
public ResultBean download(String bucket, String key){
if(StringUtil.isEmpty(bucket) || StringUtil.isEmpty(key)) return ResultUtil.resultFail("参数错误");
GetObjectRequest objectRequest = GetObjectRequest.builder().bucket(bucket).key(key).build();
GetObjectPresignRequest objectPresignRequest = GetObjectPresignRequest.builder()
.signatureDuration(Duration.ofMinutes(10))
.getObjectRequest(objectRequest)
.build();
PresignedGetObjectRequest presignedGetObjectRequest = s3Presigner.presignGetObject(objectPresignRequest);
String url = presignedGetObjectRequest.url().toString();
return ResultUtil.resultSuccess(url);
}
/**
* 对象删除,支持批量删除
* @param bucket bucket
* @param keyList 多个key组成的json数组转化成list对象
*/
public ResultBean delete(String bucket, List<AwsS3ObjectVO> keyList){
if(keyList == null || keyList.size() <= 0) return ResultUtil.resultFail("参数错误");
List<ObjectIdentifier> identifierList = new ArrayList<>();
for(AwsS3ObjectVO vo : keyList){
identifierList.add(ObjectIdentifier.builder().key(vo.getKey()).build());
}
try{
Delete delete = Delete.builder().objects(identifierList).build();
DeleteObjectsRequest deleteObjectRequest = DeleteObjectsRequest.builder().bucket(bucket).delete(delete).build();
s3Client.deleteObjects(deleteObjectRequest);
return ResultUtil.resultSuccess();
} catch (Exception ex){
throw new BizException(ex.getMessage(), ex);
}
}
4、对象存储线程池配置,上传文件时用到
/**
* 对象存储线程池配置类
*/
@EnableAsync
@Configuration
public class ThreadPoolConfig {
@Bean("awsThreadPoolExecutor")
public ThreadPoolTaskExecutor awsThreadPoolExecutor(){
// cpu参数
int cpuCount = Runtime.getRuntime().availableProcessors();
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
// 核心线程数
taskExecutor.setCorePoolSize(cpuCount);
// 最大线程数
taskExecutor.setMaxPoolSize(cpuCount * 2);
// 任务队列容量
taskExecutor.setQueueCapacity(128);
// 空闲队列存活时间
taskExecutor.setKeepAliveSeconds(20);
// 线程前缀
taskExecutor.setThreadNamePrefix("awsTaskExecutor-");
// 拒绝策略
taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 任务完成后自动关闭线程池
taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
// 初始化
taskExecutor.initialize();
return taskExecutor;
}
}
工具类ResultUtil、ResultBean和ResultPageInfo(分页需引入谷歌的pagehelper插件)
public class ResultUtil {
/**
* 分页查询成功
*/
public static <T> ResultPageInfo<T> pageSuccess(List<T> list){
ResultPageInfo<T> bean = new ResultPageInfo();
if(CollectionUtils.isEmpty(list)){
bean.setCurrentPage(0);
bean.setPageSize(0);
bean.setData(list);
bean.setTotalCount(0);
bean.setCode("0");
bean.setMsg("success");
} else {
PageInfo<T> page = new PageInfo<T>(list);
bean.setCurrentPage(page.getPageNum());
bean.setPageSize(page.getPageSize());
bean.setData(page.getList());
bean.setTotalCount(Integer.parseInt(page.getTotal() + ""));
bean.setTotalPage(page.getPages());
bean.setCode("0");
bean.setMsg("success");
}
return bean;
}
/**
* 操作成功
*/
public static <T> ResultBean<T> resultSuccess(T t){
ResultBean<T> resultBean = new ResultBean();
resultBean.setCode("0");
resultBean.setMsg("success");
resultBean.setData(t);
return resultBean;
}
/**
* 操作成功
*/
public static <T> ResultBean<T> resultSuccess(){
ResultBean<T> resultBean = new ResultBean();
resultBean.setCode("0");
resultBean.setMsg("success");
return resultBean;
}
/**
* 操作失败
*/
public static <T> ResultBean<T> resultFail(String errMsg){
ResultBean<T> resultBean = new ResultBean();
resultBean.setCode("-1");
resultBean.setMsg(errMsg);
return resultBean;
}
}
@Data
public class ResultBean<T> implements Serializable {
private static final long serialVersionUID = 160311355525594862L;
private String code = "0";
private String msg = "success";
private T data;
}
/**
* 返回分页数据
*/
@Data
public class ResultPageInfo<T> implements Serializable {
private static final long serialVersionUID = 160311355525522362L;
private String code = "0";
private String msg = "success";
private List<T> data;
/**
* 分页页码
*/
private Integer currentPage = 1;
/**
* 分页大小
*/
private Integer pageSize;
/**
* 总页数
*/
private Integer totalPage;
/**
* 总记录条数
*/
private Integer totalCount;
}
自定义异常类BizException
/**
* 业务逻辑通用异常
*/
public class BizException extends RuntimeException {
static final long serialVersionUID = -7034897190745766802L;
private String errorCode;
public BizException(String message) {
super(message);
}
public BizException(String errorCode, String message) {
super(message);
setErrorCode(errorCode);
}
public BizException(String errorCode, String message, Throwable cause) {
super(message, cause);
setErrorCode(errorCode);
}
public BizException(String errorCode, Throwable cause) {
super(cause);
setErrorCode(errorCode);
}
public String getErrorCode() {
return errorCode;
}
public void setErrorCode(String errorCode) {
this.errorCode = errorCode;
}
}
全局异常捕捉类GlobalExceptionHandler
/**
* 全局异常捕捉
*/
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 默认异常的捕捉
*/
@ExceptionHandler({Throwable.class})
public ResultBean exception(Throwable e){
log.error("系统异常", e);
return ResultUtil.resultFail(e.getMessage());
}
/**
* 自定义业务异常的捕捉
*/
@ExceptionHandler({BizException.class})
public ResultBean exception(BizException e){
log.error("业务异常", e);
return ResultUtil.resultFail(e.getErrorCode(), e.getMessage());
}
}