原因:
最近项目需要对接AWS平台,上传文件到AWS S3,按照官方文档对接,经历不少波折,所幸,最终完成了业务要求,成功对接AWS S3,防止以后忘记,特此记录一下
前提
对接AWS S3,需要满足一下前提条件
1、在AWS开通S3并且创建存储桶【bucket】
如果想让你的文件能够公共读,需要设置“权限-》屏蔽公共访问权限-》阻止所有公开访问:关闭”
2、提前准备IAM子用户以及相关权限策略
2.1 创建上传文件权限策略
示例:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"s3:PutObject"
],
"Resource": [
"*"
]
}
]
}
2.2 创建获取STS Token的权限
备注:如果不需要使用STS Token,请跳过此步骤
示例:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"sts:GetAccessKeyInfo",
"sts:GetSessionToken",
"sts:AssumeRole"
],
"Resource": [
"*"
]
}
]
}
备注:【示例只提供了上传文件和获取STS Token的权限,如果需要更多权限,请按照官方文档增加】
2.2 创建IAM用户和访问密钥
创建一个IAM子用户,将2.1创建的策略授权给IAM子用户;
创建成功后,再创建一个访问密钥,记得在创建成功保存,如果不保存,在其他地方无法获取
按照SDK开发:上传文件和获取STS Token
本文示例,使用java语言,springboot开发
1、导入jar
<!-- AWS S3 文件上传必须的jar -->
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-s3</artifactId>
</dependency>
<!-- 提供给客户端临时访问凭据,STS Token -->
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>sts</artifactId>
</dependency>
<dependencyManagement>
<dependencies>
<!-- 设置统一的版本,防止手动设置版本导致的兼容和冲突问题 -->
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>bom</artifactId>
<version>2.21.20</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<!-- 设置maven的版本,AWS SDK V2版本,要求maven版本大于3.0.0-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
<configuration>
<groups>IntegrationTest</groups>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<!-- maven 3.6.1以后的版本,不支持HTTP仓库地址,需要手动指定信任的仓库地址-->
<repository>
<id>spring-snapshots</id>
<url>http://repo.spring.io/libs-snapshot</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-snapshots</id>
<url>http://repo.spring.io/libs-snapshot</url>
</pluginRepository>
</pluginRepositories>
2、配置文件
#aws-s3
aws.s3.status=true
aws.s3.accessKey=
aws.s3.secretKey=
aws.s3.bucketName=
aws.s3.region=us-east-1
aws.s3.uploadPath=
aws.s3.viewPath=https://{s3实例Id}.amazonaws.com/
aws.s3.fileRoot=xxxx/xxx
#获取STS Token的配置
aws.sts.roleArn=arn:aws:iam::{iamId}:role/{roleName}
aws.sts.roleSessionName={roleSessionName}
3、上传文件工具类
为了使用移植方便,直接使用静态组件
package com.test.common.cloudPlatform.aws.s3;
import com.aliyun.oss.OSSException;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.model.CopyObjectResult;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.PutObjectResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.sts.StsClient;
import software.amazon.awssdk.services.sts.model.AssumeRoleRequest;
import software.amazon.awssdk.services.sts.model.AssumeRoleResponse;
import software.amazon.awssdk.services.sts.model.Credentials;
import software.amazon.awssdk.services.sts.model.StsException;
import javax.annotation.PostConstruct;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.*;
@Component
public class AWSS3Util {
private Logger logger = LoggerFactory.getLogger(AWSS3Util.class);
public static final String HTMLHEAD = "<!DOCTYPE html><html><head><meta charset='utf-8'><meta name='viewport' content='width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no'/><title>FitTrack</title><style rel='stylesheet' type='text/css'> body{margin: 16px;} img{max-width:100%;margin:auto;}</style></head><body>";
public static final String HTMLFOOT = "</body></html>";
@Value("${aws.s3.accessKey}")
private String accessKeyT;
private static String accessKey;
@Value("${aws.s3.secretKey}")
private String secretKeyT;
private static String secretKey;
@Value("${aws.s3.bucketName}")
private String bucketNameT;
private static String bucketName;
@Value("${aws.s3.region}")
private String regionT;
private static String region;
@Value("${aws.s3.uploadPath}")
private String uploadPathT;
private static String uploadPath;
@Value("${aws.s3.viewPath}")
private String viewPathT;
private static String viewPath;
@Value("${aws.s3.fileRoot}")
private String fileRootT;
private static String fileRoot;
@Value("${aws.sts.roleArn}")
private String roleArnT;
private static String roleArn;
@Value("${aws.sts.roleSessionName}")
private String roleSessionNameT;
private static String roleSessionName;
/**
* 初始化方法,使用@PostConstruct注解能够在对象通过@Component初始化后调用该方法
* ,将通过@Value加载的值赋值给静态变量
*/
@PostConstruct
private void init(){
accessKey = accessKeyT;
secretKey = secretKeyT;
bucketName = bucketNameT;
region = regionT;
uploadPath = uploadPathT;
viewPath = viewPathT;
fileRoot = fileRootT;
roleArn = roleArnT;
roleSessionName = roleSessionNameT;
}
/**
* 上传base64图片文件
* @param queryRequest 文件
* @return
*/
public static String upLoadBase64Image(ImageRequest queryRequest) {
MultipartFile file = BASE64DecodedMultipartFile.base64ToMultipart(queryRequest.getImgBase64());
BasicAWSCredentials cred = new BasicAWSCredentials(accessKey, secretKey);
AmazonS3 amazonS3 = AmazonS3ClientBuilder.standard()
.withCredentials(new AWSStaticCredentialsProvider(cred))
.withRegion(region)
.build();
String filePath = uploadPath + getFilename(queryRequest.getImgPath(), "",queryRequest.getAutoImgFilePath());
if(queryRequest.getAutoPathStatus() != null && queryRequest.getAutoPathStatus() == 1){
filePath = queryRequest.getImgPath();
}
// 设置上传文件大小
ObjectMetadata meta = new ObjectMetadata();
meta.setContentLength(file.getSize());
meta.setContentType("image/png");
if(!StringSelfUtil.isEmpty(queryRequest.getImgConfig())){
//防止中文乱码,进行base64编码
String imgConfig = Base64.getEncoder().encodeToString(queryRequest.getImgConfig().getBytes());
meta.setHeader("x-amz-meta-config",imgConfig);
}
// 上传文件
try {
amazonS3.putObject(bucketName, filePath, file.getInputStream(), meta);
} catch (Exception e) {
e.printStackTrace();
} finally {
// amazonS3.shutdown();
}
return viewPath+filePath;
}
public static String saveHtml(String htmlText) {
String filePath =getFilename(UUID.randomUUID().toString()+".html", "",0);
htmlText = HTMLHEAD +htmlText+HTMLFOOT;
BasicAWSCredentials cred = new BasicAWSCredentials(accessKey, secretKey);
AmazonS3 client = AmazonS3ClientBuilder.standard()
.withCredentials(new AWSStaticCredentialsProvider(cred))
.withRegion(region)
.build();
// 设置上传文件大小
ObjectMetadata meta = new ObjectMetadata();
meta.setContentType("text/html;charset=UTF-8");
// 保存html文件
try {
client.putObject(bucketName, filePath,new ByteArrayInputStream(htmlText.getBytes()),meta);
}catch (Exception e){
}finally {
// client.shutdown();
}
return viewPath+filePath;
}
// 生成文件名加目录
public static String getFilename(String dir,String suffix,Integer autoImgFilePath){
// 使用uuid生成唯一文件名
String cuurDdate = DateUtils.format(DateUtils.currentDate(), DateUtils.DATE_FORMAT_PATTERN);
String fileRootTemp = "";
//使用自定义地址
if(autoImgFilePath != null && autoImgFilePath == 1){
fileRootTemp = fileRoot;
}else{
fileRootTemp = fileRoot+"/"+cuurDdate;
}
if(StringSelfUtil.isEmpty(suffix)){
return fileRootTemp+"/"+dir;
}else{
return fileRootTemp+"/"+dir+"."+suffix;
}
}
public static Map getStsToken( ) {
final String usage = "";
// Region region = Region.US_EAST_1;
System.setProperty("aws.accessKeyId",accessKey);
System.setProperty("aws.secretAccessKey",secretKey);
StsClient stsClient = StsClient.builder()
.region(Region.of(region))
.build();
try {
AssumeRoleRequest roleRequest = AssumeRoleRequest.builder()
.roleArn(roleArn)
.roleSessionName(roleSessionName)
.build();
AssumeRoleResponse roleResponse = stsClient.assumeRole(roleRequest);
Credentials myCreds = roleResponse.credentials();
// Convert the Instant to readable date.
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT)
.withLocale(Locale.US)
.withZone(ZoneId.systemDefault());
Map<String,Object> result = new HashMap<>();
result.put("accessKeyId",myCreds.accessKeyId());
result.put("accessKeySecret",myCreds.secretAccessKey());
result.put("stsToken",myCreds.sessionToken());
result.put("expiration",formatter.format(myCreds.expiration()));
result.put("region",region);
result.put("bucket",bucketName);
return result ;
} catch (StsException e) {
System.err.println(e.getMessage());
}
stsClient.close();
return null;
}
/**
* 拷贝文件
* @param sourceObjectName
*/
public static void copyObject(String sourceObjectName,String destinationObjectName) {
BasicAWSCredentials cred = new BasicAWSCredentials(accessKey, secretKey);
AmazonS3 client = AmazonS3ClientBuilder.standard()
.withCredentials(new AWSStaticCredentialsProvider(cred))
.withRegion(region)
.build();
sourceObjectName = fileRoot+sourceObjectName;
destinationObjectName = fileRoot+destinationObjectName;
// 拷贝文件。
CopyObjectResult result = client.copyObject(bucketName, sourceObjectName, bucketName, destinationObjectName);
//System.out.println("ETag: " + result.getETag() + " LastModified: " + result.getLastModified());
// 关闭OSSClient。
// client.shutdown();
}
/**
* 上传文本内容
* @param filePath
* @return
*/
public static String saveTxtToFile(String filePath,String text) {
filePath = fileRoot+filePath;
BasicAWSCredentials cred = new BasicAWSCredentials(accessKey, secretKey);
AmazonS3 client = AmazonS3ClientBuilder.standard()
.withCredentials(new AWSStaticCredentialsProvider(cred))
.withRegion(region)
.build();
try {
// 设置文件Content-Type
String suffix = filePath.substring(filePath.lastIndexOf(".")+1,filePath.length());
if(suffix!=null){
ObjectMetadata meta = new ObjectMetadata();
meta.setContentType("text/plain");
// 保存js文件
if(suffix.equals("js")){
meta.setContentType("application/javascript");
}
if(suffix.equals("css")){
meta.setContentType("text/css");
}
client.putObject(bucketName, filePath,new ByteArrayInputStream(filePath.getBytes()),meta);
}else{
client.putObject(bucketName, filePath,text);
}
} catch (OSSException e) {
e.printStackTrace();
}catch (Exception e) {
e.printStackTrace();
} finally {
// client.shutdown();
}
return viewPath+filePath;
}
}