AWS S3 踩坑 第一次用S3,记录一下
1. Key在哪里
IAM 设置一个用户组,权限勾选S3 的选项
生成一个专属用户, 划分到用户组
用户处生成密钥,那AccessKey 和 SecretKey
2. 桶策略配置 1 2 3 4 5 6 7 8 9 10 11 { "Version" : "2012-10-17" , "Statement" : [ { "Effect" : "Allow" , "Principal" : "*" , "Action" : "s3:GetObject" , "Resource" : "arn:aws:s3:::桶名称/*" } ] }
3. Spring Boot怎么连接
Maven 找到AWS SDK S3 导入1 2 3 4 5 6 <dependency > <groupId > com.amazonaws</groupId > <artifactId > aws-java-sdk-s3</artifactId > <version > 1.12.696</version > </dependency >
application 填写Region,两个Key1 2 3 4 5 6 amazon: s3: accessKey: secretKey: region: bucketName:
构建单例工具类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 @Slf4j @Component public class AmazonS3Util { private static String accessKey; private static String secretKey; private static String region; @Value("${amazon.s3.accessKey}") public void setAccessKey (String accessKey) { AmazonS3Util.accessKey = accessKey; } @Value("${amazon.s3.secretKey}") public void setSecretKey (String secretKey) { AmazonS3Util.secretKey = secretKey; } @Value("${amazon.s3.region}") public void setRegion (String region) { AmazonS3Util.region = region; } @Value("${amazon.s3.bucketName}") public String bucketName; public static volatile AmazonS3 client; public static AmazonS3 getInstance () { if (Objects.isNull(client)) { synchronized (AmazonS3.class) { if (Objects.isNull(client)) { AWSCredentials credentials = new BasicAWSCredentials (accessKey, secretKey); ClientConfiguration conf = new ClientConfiguration (); conf.setMaxConnections(200 ); conf.setSocketTimeout(10000 ); conf.setMaxErrorRetry(1 ); conf.setProtocol(Protocol.HTTPS); conf.setSignerOverride("AWSS3V4SignerType" ); return AmazonS3ClientBuilder.standard() .withCredentials(new AWSStaticCredentialsProvider (credentials)) .withRegion(region) .withClientConfiguration(conf) .build(); } } } return client; } public String uploadFile (String contextType, String fileName, MultipartFile file) { try (InputStream input = new ByteArrayInputStream (file.getBytes())) { ObjectMetadata metadata = new ObjectMetadata (); metadata.setContentType(contextType); metadata.setHttpExpiresDate(new Date (System.currentTimeMillis() + TimeUnit.DAYS.toMillis(365 ))); metadata.setContentLength(input.available()); fileName = UUID.randomUUID().toString().replace("-" , "" ) + fileName; getInstance().putObject(bucketName, fileName, input, metadata); return fileName; } catch (Exception e) { log.error("上传文件失败" , e); } return null ; } public String generateResignedUrl (String fileName) { URL url = getInstance().generatePresignedUrl(bucketName, fileName, new Date (System.currentTimeMillis() + TimeUnit.DAYS.toMillis(7 )), HttpMethod.GET); return url.toString(); } }
4. 公网访问文件,是否必须关闭网络访问 屏蔽公共访问权限(存储桶设置) 阻止所有公开访问,获取授权访问可以调用 generatePresignedUrl 但是有时效,S3 SDK获取公网链接,最长7天
5. 使用时遇到的坑 本来想构建几个目录,划分不同职责,之前公司华为云、阿里云上都可以这么做,但是S3 这里有问题,我可以在bucket下增删除查改数据,但是如果我新建目录 image/ 再在这个目录下进行数据CURD操作,则报 SignatureDoesNotMatch
错误.
1 2 <Code > SignatureDoesNotMatch</Code > <Message > The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message >
我一直以为是我Key有问题,因为一开始我测试文件是放bucket下的,新增目录肯定是我的问题,但是后面在github上发现有人说,配置目录后就是会这样的。
规避方法 文件名添加UUID前缀,放在bucket下。 如果有其他方法,请告知我,谢谢。
其他记录
MultipartFile 中文名乱码,用ISO_8859_1 读取文件名并使用UTF8记录1 2 3 4 5 6 7 8 9 public static String getFileName (MultipartFile file) { try { return new String (Objects.requireNonNull(file.getOriginalFilename()).getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8); } catch (Exception e) { return file.getOriginalFilename(); } }