springboot集成对象存储aws java v2

归松
2023-12-01

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());
    }
}

 类似资料: