SpringBoot 2.x集成AWS S3对象存储
1、pom配置
<!--锁定对象存储版本--> <dependencyManagement> <dependencies> <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>bom</artifactId> <version>2.17.136</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <!--对象存储 S3 start--> <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>s3</artifactId> </dependency> <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>s3-transfer-manager</artifactId> <version>2.17.103-PREVIEW</version> </dependency> <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>kms</artifactId> </dependency> <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>s3control</artifactId> </dependency> <!--对象存储 S3 end-->
2、application.yml增加配置
#AWS S3配置
aws:
s3:
accessKeyId: A81BFFE58AKIBB950
secretKey: Fkb7Yw3aIMAPyst7c5ISGthSKi4mfBDk09HS
s3Uri: https://s3.test.com
bucket: test-bucket
3、FileS3Util工具类
import cn.hutool.core.util.StrUtil; import com.test.common.constant.CommonConstant; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.springframework.web.multipart.MultipartFile; import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; import software.amazon.awssdk.core.sync.RequestBody; 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.model.*; import software.amazon.awssdk.services.s3.presigner.S3Presigner; import software.amazon.awssdk.services.s3.presigner.model.GetObjectPresignRequest; import software.amazon.awssdk.services.s3.presigner.model.PresignedGetObjectRequest; import javax.annotation.PostConstruct; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStream; import java.net.URI; import java.text.SimpleDateFormat; import java.time.Duration; import java.util.*; @Slf4j @Component public class FileS3Util { @Value("${aws.s3.accessKeyId}") private String accessKeyId; @Value("${aws.s3.secretKey}") private String secretKey; @Value("${aws.s3.s3Uri}") private String s3Uri; @Value("${aws.s3.bucket}") private String bucketName; public static String S3_ACCESS_KEY_ID = null; public static String S3_SECRET_KEY = null; public static String S3_URI = null; public static String S3_BUCKET = null; @PostConstruct public void init() { S3_ACCESS_KEY_ID = accessKeyId; S3_SECRET_KEY = secretKey; S3_URI = s3Uri; S3_BUCKET = bucketName; } private static S3Client s3Client; private static S3Presigner s3Presigner; /** * @description: 获取S3客户端对象 * @author: bug * @date: 2022/6/23 19:45 * @param * @return software.amazon.awssdk.services.s3.S3Client */ public static synchronized S3Client getS3Client() { if (null == s3Client) { s3Client = S3Client.builder() .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create(S3_ACCESS_KEY_ID, S3_SECRET_KEY))) .endpointOverride(URI.create(S3_URI)) .serviceConfiguration(item -> item.pathStyleAccessEnabled(true).checksumValidationEnabled(false)) .region(Region.AP_SOUTHEAST_1) .build(); } return s3Client; } /** * @description: 获取预签名对象 * @author: bug * @date: 2022/6/23 19:46 * @param * @return software.amazon.awssdk.services.s3.presigner.S3Presigner */ public static synchronized S3Presigner getS3PreSigner() { if (null == s3Presigner) { s3Presigner = S3Presigner.builder() .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create(S3_ACCESS_KEY_ID, S3_SECRET_KEY))) .endpointOverride(URI.create(S3_URI)) .serviceConfiguration(S3Configuration.builder() .checksumValidationEnabled(false) .pathStyleAccessEnabled(true) .build()) .region(Region.AP_SOUTHEAST_1) .build(); } return s3Presigner; } /** * @description: 根据文件地址上传文件 * @author: bug * @date: 2022/6/23 19:39 * @param filePath 文件绝对地址 * @param pathName 目标目录: test * @return java.lang.String */ public static String uploadPublicFile(String filePath, String pathName) { boolean failFlag = true; String objectKey = ""; boolean exists = FileUtils.exists(filePath); if (exists) { File file = new File(filePath); InputStream inputStream = null; try { inputStream = new FileInputStream(file); //获取文件后缀 String suffix = StrUtil.subAfter(filePath, ".", true); objectKey = getFileName(suffix, pathName); PutObjectRequest putObjectRequest = PutObjectRequest.builder() .bucket(S3_BUCKET) .key(objectKey) .acl(ObjectCannedACL.PUBLIC_READ) .build(); S3Client s3Client = getS3Client(); PutObjectResponse putObjectResponse = s3Client.putObject(putObjectRequest, RequestBody.fromInputStream(inputStream, file.length())); failFlag = false; } catch (FileNotFoundException e) { log.error("上传S3文件对象转流失败,异常:{}", e); } catch (Exception e) { log.error("上传S3文件失败,异常:{}", e); } } if (failFlag) { objectKey = ""; } return objectKey; } /** * @description: 根据文件对象上传文件 * @author: bug * @date: 2022/6/23 19:38 * @param multipartFile 文件对象 * @param pathName 目标目录 * @return java.lang.String */ public static String uploadPublicFile(MultipartFile multipartFile, String pathName) { S3Client s3Client = getS3Client(); String s3FileFullPath = getFileName(multipartFile, pathName); try { PutObjectRequest putObjectRequest = PutObjectRequest.builder() .bucket(S3_BUCKET) .contentType(multipartFile.getContentType()) .contentLength(multipartFile.getSize()) .key(s3FileFullPath) .acl(ObjectCannedACL.PUBLIC_READ) .build(); PutObjectResponse putObjectResponse = s3Client.putObject(putObjectRequest, RequestBody.fromInputStream(multipartFile.getInputStream(), multipartFile.getSize())); return s3FileFullPath; } catch (Exception e) { log.error("上传文件到S3失败 异常:{}", e); } return null; } /** * @description: 根据文件对象和目标目录生成新的文件地址 * @author: bug * @date: 2022/6/23 19:40 * @param multipartFile 上传文件 * @param pathName 目标目录 * @return java.lang.String */ private static String getFileName(MultipartFile multipartFile, String pathName) { String suffix = multipartFile.getOriginalFilename().substring(multipartFile.getOriginalFilename().lastIndexOf(".") + 1); return getFileName(suffix, pathName); } /** * @description: 根据文件后缀和目标目录生成新的文件地址 * @author: bug * @date: 2022/6/23 19:41 * @param suffix 文件后缀 * @param pathName 目标目录 test * @return java.lang.String test/2022/06 */ private static String getFileName(String suffix, String pathName) { //生成随机文件名 String localFileName = UUID.randomUUID() + "." + suffix; Date date = new Date(); SimpleDateFormat formatter_yyyy = new SimpleDateFormat("yyyy"); SimpleDateFormat formatter_MM = new SimpleDateFormat("MM"); //在随机名前加上年月 String s3FileFullPath = pathName + "/" + formatter_yyyy.format(date) + "/" + formatter_MM.format(date) + "/" + localFileName; return s3FileFullPath; } /** * @description: 生成预签名URL * @author: bug * @date: 2022/6/23 19:42 * @param keyName key名称: test/2022/06/123.pdf * @param signatureDurationTime 有效期 单位:秒 * @return java.lang.String */ public static String getPreSignatureUrl(String keyName, Integer signatureDurationTime) { String preSignatureUrl = ""; try { S3Presigner s3PreSigner = FileS3Util.getS3PreSigner(); GetObjectRequest getObjectRequest = GetObjectRequest.builder() .bucket(S3_BUCKET) .key(keyName) .build(); //设置预签名URL可访问时间 signatureDurationTime = Optional.ofNullable(signatureDurationTime) .map(item -> { if (item.intValue() > CommonConstant.Numbers.NUMBER_10080) { item = CommonConstant.Numbers.NUMBER_10080; } return item; }) .orElse(CommonConstant.Numbers.NUMBER_10); GetObjectPresignRequest getObjectPresignRequest = GetObjectPresignRequest.builder() .signatureDuration(Duration.ofMinutes(signatureDurationTime)) .getObjectRequest(getObjectRequest) .build(); PresignedGetObjectRequest presignedGetObjectRequest = s3PreSigner.presignGetObject(getObjectPresignRequest); preSignatureUrl = String.valueOf(presignedGetObjectRequest.url()); } catch (Exception e) { log.error("生成预签名URL失败,异常:{}", e); } return preSignatureUrl; } /** * 获取S3中对象的信息,最多只能返回1000个 * * @return */ public static List<Map<String, Object>> listObject() { ListObjectsRequest listObjects = ListObjectsRequest .builder() .bucket(S3_BUCKET) .build(); S3Client s3Client = FileS3Util.getS3Client(); ListObjectsResponse res = s3Client.listObjects(listObjects); List<S3Object> objects = res.contents(); List<Map<String, Object>> result = Lists.newArrayList(); for (ListIterator iterVals = objects.listIterator(); iterVals.hasNext(); ) { S3Object myValue = (S3Object) iterVals.next(); Map<String, Object> map = Maps.newHashMap(); map.put("objectKey", myValue.key()); map.put("objectSize(/KBs)", myValue.size() / 1024); map.put("ownerId", myValue.owner().id()); map.put("ownerDisplayName", myValue.owner().displayName()); result.add(map); } return result; } /** * 获取S3中对象的信息,最多只能返回1000个 * * @param delimiter 分隔符号 * @return */ public static List<Map<String, Object>> listObjectByDelimiter(String delimiter, Integer maxKeys) { //限制返回对象信息数量 maxKeys = Optional.ofNullable(maxKeys) .map(item -> { if (item.intValue() > CommonConstant.Numbers.NUMBER_1000) { item = CommonConstant.Numbers.NUMBER_1000; } return item; }) .orElse(CommonConstant.Numbers.NUMBER_1000); ListObjectsRequest listObjects = ListObjectsRequest .builder() .bucket(S3_BUCKET) .delimiter(delimiter) .maxKeys(maxKeys) .build(); S3Client s3Client = FileS3Util.getS3Client(); ListObjectsResponse res = s3Client.listObjects(listObjects); List<S3Object> objects = res.contents(); List<Map<String, Object>> result = Lists.newArrayList(); for (ListIterator iterVals = objects.listIterator(); iterVals.hasNext(); ) { S3Object myValue = (S3Object) iterVals.next(); Map<String, Object> map = Maps.newHashMap(); map.put("objectKey", myValue.key()); map.put("objectSize(/KBs)", myValue.size() / 1024); map.put("ownerId", myValue.owner().id()); map.put("ownerDisplayName", myValue.owner().displayName()); result.add(map); } return result; } /** * 获取S3中对象的信息,最多只能返回1000个 * * @param prefix 前缀 * @return */ public static List<Map<String, Object>> listObjectByPrefix(String prefix, Integer maxKeys) { //限制返回对象信息数量 maxKeys = Optional.ofNullable(maxKeys) .map(item -> { if (item.intValue() > CommonConstant.Numbers.NUMBER_1000) { item = CommonConstant.Numbers.NUMBER_1000; } return item; }) .orElse(CommonConstant.Numbers.NUMBER_1000); ListObjectsRequest listObjects = ListObjectsRequest .builder() .bucket(S3_BUCKET) .prefix(prefix) .maxKeys(maxKeys) .build(); S3Client s3Client = FileS3Util.getS3Client(); ListObjectsResponse res = s3Client.listObjects(listObjects); List<S3Object> objects = res.contents(); List<Map<String, Object>> result = Lists.newArrayList(); for (ListIterator iterVals = objects.listIterator(); iterVals.hasNext(); ) { S3Object myValue = (S3Object) iterVals.next(); Map<String, Object> map = Maps.newHashMap(); map.put("objectKey", myValue.key()); map.put("objectSize(/KBs)", myValue.size() / 1024); map.put("ownerId", myValue.owner().id()); map.put("ownerDisplayName", myValue.owner().displayName()); result.add(map); } return result; } /** * 获取S3中对象的信息,最多只能返回1000个 * * @param delimiter 分隔符 * @param prefix 前缀 * @param maxKeys 返回最大的对象信息 1000个 * @return */ public static List<Map<String, Object>> listObjectByDelimiterWithPrefix(String delimiter, String prefix, Integer maxKeys) { //限制返回对象信息数量 maxKeys = Optional.ofNullable(maxKeys) .map(item -> { if (item.intValue() > CommonConstant.Numbers.NUMBER_1000) { item = CommonConstant.Numbers.NUMBER_1000; } return item; }) .orElse(CommonConstant.Numbers.NUMBER_1000); ListObjectsRequest listObjects = ListObjectsRequest .builder() .bucket(S3_BUCKET) .delimiter(delimiter) .prefix(prefix) .maxKeys(maxKeys) .build(); S3Client s3Client = FileS3Util.getS3Client(); ListObjectsResponse res = s3Client.listObjects(listObjects); List<S3Object> objects = res.contents(); List<Map<String, Object>> result = Lists.newArrayList(); for (ListIterator iterVals = objects.listIterator(); iterVals.hasNext(); ) { S3Object myValue = (S3Object) iterVals.next(); Map<String, Object> map = Maps.newHashMap(); map.put("objectKey", myValue.key()); map.put("objectSize(/KBs)", myValue.size() / 1024); map.put("ownerId", myValue.owner().id()); map.put("ownerDisplayName", myValue.owner().displayName()); result.add(map); } return result; } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
2021-06-23 JAVA 获取某天、某周、某月、某年的开始时间和结束时间