文章目录
STS临时访问OSS
什么是STS?
阿里云STS(Security Token Service)是阿里云提供的一种临时访问权限管理服务。RAM提供RAM用户和RAM角色两种身份。其中,RAM角色不具备永久身份凭证,而只能通过STS获取可以自定义时效和访问权限的临时身份凭证,即安全令牌(STS Token)。
即,阿里云提供的一个服务。我们需要创建一个RAM角色,然后通过STS赋予这个角色一定时间的权限去操作oss。
什么又是RAM角色?
RAM角色是一种虚拟用户,可以被授予一组权限策略。与RAM用户不同,RAM角色没有确定的登录密码或访问密钥,它需要被一个可信的实体用户(RAM用户、阿里云服务或身份提供商)扮演。扮演成功后实体用户将获得RAM角色的临时身份凭证,即安全令牌(STS Token),使用安全令牌就能以RAM角色身份访问被授权的资源。
详细说明,参见官网:https://help.aliyun.com/document_detail/28756.html
OSS中的几大名词
1.Bucket
存储空间
存储空间是用户用于存储对象(Object)的容器,所有的对象都必须隶属于某个存储空间。存储空间具有各种配置属性,包括地域、访问权限、存储类型等。用户可以根据实际需求,创建不同类型的存储空间来存储不同的数据。
省流:放文件的容器
2.Object
文件对象
对象是 OSS 存储数据的基本单元,也被称为 OSS 的文件。对象由元信息(Object Meta),用户数据(Data)和文件名(Key)组成。对象由存储空间内部唯一
的 Key 来标识。对象元信息是一组键值对,表示了对象的一些属性,比如最后修改时间、大小等信息,同时用户也可以在元信息中存储一些自定义的信息。
省流:一个文件上传后就是一个文件对象(元信息+文件名+文件数据)
3.Region
地域
这个就很好理解了。表示OSS数据中心所在的位置。
region 格式一般是 oss- 国家 - 地域,比如 oss-cn-beijing ,就是代表存储的物理位置是在北京。
region在创建bucket时选择,选择离自己近的地方速度会快一些
4.EndPoint
访问域名
Endpoint 表示 OSS 对外服务的访问域名。OSS 以 HTTP RESTful API 的形式对外提供服务,当访问不同的 Region 的时候,需要不同的域名。通过内网和外网访问同一个 Region 所需要的 Endpoint 也是不同的。例如杭州 Region 的外网Endpoint 是 oss-cn-hangzhou.aliyuncs.com,内网 Endpoint 是 oss-cn-hangzhou-internal.aliyuncs.com。具体的内容请参见各个 Region 对应的 Endpoint。
5.bucket 公网域名
指的是 bucket 和 endpoint 结合在一起,让用户访问的公网域名。完整的格式是 bucket.endpoint ,比如我的 bucket 是 xiaoming,endpoint 在杭州(oss-cnhangzhou.aliyuncs.com),那么我的 bucket 公网域名就是 xiaoming.oss-cn-hangzhou.aliyuncs.com。
6.AccessKey
访问秘钥
访问秘钥包括:AccessKeyId 和 AccessKeySecret
创建Bucket
登录阿里云—右上角控制台—进入对象存储OSS(没用过oss,可以搜索oss再进入)
点击bucket列表(左上角)—创建:
名字-------
地域--------选离你近的
存储类型-------低频访问存储即可
读写权限-------私有
服务端加密----无
其余不管。
确定即可
如何获取STS?
下文按照官方步骤,仅配上大量图,官方:https://help.aliyun.com/document_detail/100624.htm?spm=a2c4g.11186623.0.0.5e0c50c597pv2Q#concept-xzh-nzk-2gb
步骤一:创建RAM角色
- 登录阿里云—>搜索框搜索
访问控制
—>进入产品控制台—>左侧身份管理的用户
- 创建用户
LTAI5tBdhZewKPL1qbsCHg1K
hLy6RYkYSe8JGpohjTcRf86LuWCOCu
步骤二:为RAM用户授予请求AssumeRole的权限
-
选中该用户,点击添加权限
-
搜索STS,找到AliyunSTSAssumeRoleAccess权限,点击一下就添加了
确定即可
步骤三:创建用于获取临时访问凭证的角色
-
左侧身份管理–角色–创建角色
-
选择阿里云账号,下一步
-
给角色起名,点击完成—关闭
-
点击进入刚刚创建的角色,复制其ARN,待会会用到
步骤四:为角色授予上传文件的权限
-
左侧授权管理–授权策略–创建策略
-
选择脚本编辑(当然可视化编辑也可以,这里求简单~~)
{
"Version": "1",
"Statement": [
{
"Effect": "Allow",
"Action": [
"oss:PutObject"
],
"Resource": [
"acs:oss:*:*:examplebucket/exampledir",
"acs:oss:*:*:examplebucket/exampledir/*"
]
}
]
}
复制粘贴。将这两处替换为自己的(ps:上传文件时,目录不存在,oss会自动创建)
下一步
-
取名,确定
-
回到角色页面,点击添加权限
-
选择自定义策略,选择我们刚刚创建的策略,点击即可添加。点击确定完成
ok,以上我们就创建好了RAM用户和角色。
下面就使用RAM用户的秘钥和角色的ARN去获取访问凭证
步骤四:获取临时访问凭证
这里是官方demo:
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import com.aliyuncs.sts.model.v20150401.AssumeRoleRequest;
import com.aliyuncs.sts.model.v20150401.AssumeRoleResponse;
public class StsServiceSample {
public static void main(String[] args) {
// STS接入地址,例如sts.cn-hangzhou.aliyuncs.com。
String endpoint = "<sts-endpoint>";
// 填写步骤1生成的访问密钥AccessKey ID和AccessKey Secret。
String AccessKeyId = "<yourAccessKeyId>";
String accessKeySecret = "<yourAccessKeySecret>";
// 填写步骤3获取的角色ARN。
String roleArn = "<yourRoleArn>";
// 自定义角色会话名称,用来区分不同的令牌,例如可填写为SessionTest。
String roleSessionName = "<yourRoleSessionName>";
// 以下Policy用于限制仅允许使用临时访问凭证向目标存储空间examplebucket上传文件。
// 临时访问凭证最后获得的权限是步骤4设置的角色权限和该Policy设置权限的交集,即仅允许将文件上传至目标存储空间examplebucket下的exampledir目录。
String policy = "{\n" +
" \"Version\": \"1\", \n" +
" \"Statement\": [\n" +
" {\n" +
" \"Action\": [\n" +
" \"oss:PutObject\"\n" +
" ], \n" +
" \"Resource\": [\n" +
" \"acs:oss:*:*:examplebucket/*\" \n" +
" ], \n" +
" \"Effect\": \"Allow\"\n" +
" }\n" +
" ]\n" +
"}";
try {
// regionId表示RAM的地域ID。以华东1(杭州)地域为例,regionID填写为cn-hangzhou。也可以保留默认值,默认值为空字符串("")。
String regionId = "";
// 添加endpoint。适用于Java SDK 3.12.0及以上版本。
DefaultProfile.addEndpoint(regionId, "Sts", endpoint);
// 添加endpoint。适用于Java SDK 3.12.0以下版本。
// DefaultProfile.addEndpoint("",regionId, "Sts", endpoint);
// 构造default profile。
IClientProfile profile = DefaultProfile.getProfile(regionId, AccessKeyId, accessKeySecret);
// 构造client。
DefaultAcsClient client = new DefaultAcsClient(profile);
final AssumeRoleRequest request = new AssumeRoleRequest();
// 适用于Java SDK 3.12.0及以上版本。
request.setSysMethod(MethodType.POST);
// 适用于Java SDK 3.12.0以下版本。
//request.setMethod(MethodType.POST);
request.setRoleArn(roleArn);
request.setRoleSessionName(roleSessionName);
request.setPolicy(policy); // 如果policy为空,则用户将获得该角色下所有权限。
request.setDurationSeconds(3600L); // 设置临时访问凭证的有效时间为3600秒。
final AssumeRoleResponse response = client.getAcsResponse(request);
System.out.println("Expiration: " + response.getCredentials().getExpiration());
System.out.println("Access Key Id: " + response.getCredentials().getAccessKeyId());
System.out.println("Access Key Secret: " + response.getCredentials().getAccessKeySecret());
System.out.println("Security Token: " + response.getCredentials().getSecurityToken());
System.out.println("RequestId: " + response.getRequestId());
} catch (ClientException e) {
System.out.println("Failed:");
System.out.println("Error code: " + e.getErrCode());
System.out.println("Error message: " + e.getErrMsg());
System.out.println("RequestId: " + e.getRequestId());
}
}
}
这6个需要换成我们自己的东西:
-
sts-endpoint(STS接入点)
去这里查看,你所属地的sts-endpoint。比如我在成都:
sts.cn-chengdu.aliyuncs.com
-
AccessKeyId和AccessKeySecret就是之前保存的RAM用户的访问秘钥
-
roleARN
之前保存的RAM角色的ARN。如果刚刚没保存,可在角色页面,点击进入角色查看
-
roleSessionName
区分角色会话的,可以随便取
-
policy
很明显,这就是我们为角色设置的权限策略。最简单的做法就是把bucketname改为自己的。
你也可以去权限管理–权限策略下,找到之前创建的(筛选自定义策略)策略,点击进入,复制即可。
复制的话,我担心有些新同学犯错:
String policy = ""; // 第一步 String policy = "粘贴复制内容即可"; // 第二步
替换完成,运行main方法:
恭喜你成功了。如果你的存在问题,去官网看看。或者私信我
我们拿到返回的数据后,其中AccessKeyId
、AccessKeySecret
、Security Token
是我们后续操作需要的(比如上传文件)
需要注意的是,获取到的访问凭证过期时间:
最小900秒,最大值为RAM角色的最大会话时间(可点击角色进入即可修改最大会话时间,默认3600s)
上传文件
STS返回的数据主要用于:构建OSSClient实例
// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret, securityToken);
然后用ossClient即可上传文件、下载文件、删除文件。。。。。。
如何让OSS的文件链接具有过期时间
点开上面的文档链接,下面就是介绍:使用签名URL进行临时授权
// 设置签名URL过期时间,单位为毫秒。
Date expiration = new Date(new Date().getTime() + 3600 * 1000);
// 生成以GET方法访问的签名URL,访客可以直接通过浏览器访问相关内容。
URL url = ossClient.generatePresignedUrl(bucketName, objectName, expiration);
返回给前端可以toString()一下。
这样这个链接就具有时效性了。
需要注意的是URL的过期时间最大值为32400秒。默认值为3600秒
注意点:
由于STS临时账号以及签名URL均需设置有效时长,当您使用STS临时账号生成签名URL执行相关操作(例如上传、下载文件)时,以最小的有效时长为准。例如您的STS临时账号的有效时长设置为1200秒、签名URL设置为3600秒时,当有效时长超过1200秒后,您无法使用此STS临时账号生成的签名URL上传文件。
但是,如果并不采用URL上传文件,只是用来访问文件。那么这并不是什么问题。
项目中如何封装OSS?
对于我正在写的小框架来说,我将文件单独做成一个模块,用于封装oss这些对象存储服务。
要封装oss,首先这些参数需要"可配置",将他们放在配置文件,用类读取为bean。
其次,我的做法是将ossClient的操作封装为一个OssTemplate,用于service调用。
其次,对于上传的文件(oss上的文件名(filekey)为uuid生成的随机字符串),其信息需要存入数据库。用于管理文件和crud需要。
其次,上传的文件在oss上分类存放,比如图片一个目录、视频一个目录。(比如上传的一张图片,通过后缀判定上传到image目录,将filekey—>thisisuuid
改为image/thisisuuid
,oss会将其识别为目录)
其次,需要使用redis,缓存STS返回的临时凭证(AccessKeyId
、AccessKeySecret
、Security Token
)
参数配置化
这是我的配置文件
需要一个类读取oss的配置:
/**
* @author 29443
* @date 2022/4/23
*/
@Data
@Component
@ConfigurationProperties(prefix = "oss")
public class OssProperties {
/** 上传文件需要的地域节点 */
private String endpoint;
/** 获取sts临时访问令牌需要的地域节点 */
private String stsEndpoint;
private String bucketName;
private String AccessKeyId;
private String AccessKeySecret;
/** 角色ARN */
private String roleArn;
/** 角色权限策略 */
private String policy;
/** 自定义角色会话名称,用于区分不同的令牌,可随便填写 */
private String roleSessionName;
/** 临时令牌的有效期,秒 */
@Min(value = 900, message = "临时令牌最小过期时间为900秒")
@Max(value =43200, message = "临时令牌最大过期时间43200秒")
private Long stsExpireTime;
/** 访问url的有效期,秒(默认3600s,最大32400s) */
@Max(value = 32400, message = "url有效时间最大设置为32400秒")
private Long urlExpireTime;
}
@ConfigurationProperties(prefix = "oss")
即读取配置文件中的oss开头的内容,它必须搭配@Component
向Spring容器注册
封装OSS操作
内容有点长,仔细品尝。
其实主要在于,获取STS临时访问令牌,以及将返回的凭证(AccessKeyId、AccessKeySecret、Security Token)存入redis。redis过期时间和凭证过期时间相同,都是由配置文件的stsExpireTime
决定。
而获取的文件链接过期时间由配置文件的urlExpireTime
决定。
每次进行OSS的操作时,检查redis中有不有凭证信息,有就取出来构建OssClient对象,然后才能操作文件。
redis中只要AccessKeyId、AccessKeySecret、Security Token有一个不存在,就会重新获取临时凭证。然后再从redis中取,如果还是有空值,那么构建OssClient对象的时候就会抛出异常。
package com.monkeylessey.config;
import com.aliyun.oss.HttpMethod;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.GeneratePresignedUrlRequest;
import com.aliyun.oss.model.OSSObject;
import com.aliyun.oss.model.ObjectMetadata;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.auth.sts.AssumeRoleRequest;
import com.aliyuncs.auth.sts.AssumeRoleResponse;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import com.monkeylessey.constant.RedisKeyPrefixConstants;
import com.monkeylessey.domain.entity.FileInfo;
import com.monkeylessey.properties.OssProperties;
import com.monkeylessey.utils.RedisUtil;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Date;
import java.util.concurrent.TimeUnit;
/**
* @author 29443
* @date 2022/4/23
*/
@Component
@RequiredArgsConstructor
public class OssTemplate {
private final OssProperties ossProperties;
private final RedisUtil redisUtil;
/**
* STS获取令牌、AccessId、AccessKeySecret
* @return
*/
private void getOssToken() {
String securityToken = "";
// regionId表示RAM的地域ID。以华东1(杭州)地域为例,regionID填写为cn-hangzhou。也可以保留默认值,默认值为空字符串("")。
String regionId = "cn-chengdu";
// 添加endpoint。适用于Java SDK 3.12.0及以上版本。
DefaultProfile.addEndpoint(regionId, "Sts", ossProperties.getStsEndpoint());
// 添加endpoint。适用于Java SDK 3.12.0以下版本。
// DefaultProfile.addEndpoint("",regionId, "Sts", endpoint);
// 构造default profile。
IClientProfile profile = DefaultProfile.getProfile(regionId,
ossProperties.getAccessKeyId(),
ossProperties.getAccessKeySecret());
// 构造client。
DefaultAcsClient client = new DefaultAcsClient(profile);
final AssumeRoleRequest request = new AssumeRoleRequest();
// 适用于Java SDK 3.12.0及以上版本。
request.setSysMethod(MethodType.POST);
// 适用于Java SDK 3.12.0以下版本。
//request.setMethod(MethodType.POST);
request.setRoleArn(ossProperties.getRoleArn());
request.setRoleSessionName(ossProperties.getRoleSessionName());
request.setPolicy(ossProperties.getPolicy()); // 如果policy为空,则用户将获得该角色下所有权限。
request.setDurationSeconds(ossProperties.getStsExpireTime()); // 设置临时访问凭证的有效时间为3600秒。
try {
final AssumeRoleResponse response = client.getAcsResponse(request);
System.out.println("Expiration: " + response.getCredentials().getExpiration());
System.out.println("Access Key Id: " + response.getCredentials().getAccessKeyId());
System.out.println("Access Key Secret: " + response.getCredentials().getAccessKeySecret());
System.out.println("Security Token: " + response.getCredentials().getSecurityToken());
System.out.println("RequestId: " + response.getRequestId());
// 获得的临时访问令牌
securityToken = response.getCredentials().getSecurityToken();
// RAM角色临时的AccessKeyId
String accessKeyId = response.getCredentials().getAccessKeyId();
// RAM角色临时的AccessKeySecret
String accessKeySecret = response.getCredentials().getAccessKeySecret();
redisUtil.saveForValueWithExpire(RedisKeyPrefixConstants.OSSTOKEN,
securityToken,
ossProperties.getStsExpireTime(),
TimeUnit.SECONDS);
redisUtil.saveForValueWithExpire(RedisKeyPrefixConstants.OSSACCESSKEYID,
accessKeyId,
ossProperties.getStsExpireTime(),
TimeUnit.SECONDS);
redisUtil.saveForValueWithExpire(RedisKeyPrefixConstants.OSSACCESSKEYSECRET,
accessKeySecret,
ossProperties.getStsExpireTime(),
TimeUnit.SECONDS);
} catch (ClientException e) {
// todo 抛出获取sts临时访问令牌的异常
System.out.println("Failed:");
System.out.println("Error code: " + e.getErrCode());
System.out.println("Error message: " + e.getErrMsg());
System.out.println("RequestId: " + e.getRequestId());
}
}
/**
* 获取oss客户端实例
* @return
*/
public OSS getOssClient() {
String ossToken = "";
String accessId = "";
String accessSecret = "";
ossToken = redisUtil.getValue(RedisKeyPrefixConstants.OSSTOKEN, new String());
accessId = redisUtil.getValue(RedisKeyPrefixConstants.OSSACCESSKEYID, new String());
accessSecret = redisUtil.getValue(RedisKeyPrefixConstants.OSSACCESSKEYSECRET, new String());
if (StringUtils.isBlank(ossToken) || StringUtils.isBlank(accessId) || StringUtils.isBlank(accessSecret)) {
getOssToken();
ossToken = redisUtil.getValue(RedisKeyPrefixConstants.OSSTOKEN, new String());
accessId = redisUtil.getValue(RedisKeyPrefixConstants.OSSACCESSKEYID, new String());
accessSecret = redisUtil.getValue(RedisKeyPrefixConstants.OSSACCESSKEYSECRET, new String());
}
OSS client = new OSSClientBuilder().build(ossProperties.getEndpoint(),
accessId,
accessSecret,
ossToken
);
return client;
}
/**
* 上传文件
* @param stream 文件输入流
* @param info 文件信息
*/
public void putObject(InputStream stream, FileInfo info) throws IOException {
OSS ossClient = getOssClient();
// 设置对象元信息
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentLength(info.getSize());
objectMetadata.setContentType(info.getContentType());
ossClient.putObject(ossProperties.getBucketName(), info.getFileKey(), stream, objectMetadata);
}
/**
* 获取有时效的访问链接
* @param fileKey oss上的文件名
*/
public String getHasExpireUrl(String fileKey) {
OSS ossClient = getOssClient();
// 链接过期时间和STS临时账号过期时间相同
// url过期时间和STS临时账号过期时间,两者取最小时间
// 例如您的STS临时账号的有效时长设置为1200秒、签名URL设置为3600秒时,当有效时长超过1200秒后,您无法使用此STS临时账号生成的签名URL上传文件
Date expiration = new Date(System.currentTimeMillis() + ossProperties.getUrlExpireTime() * 1000);
URL url = ossClient.generatePresignedUrl(ossProperties.getBucketName(), fileKey, expiration);
return url.toString();
}
/**
* 获取没有时效的访问链接
* @param fileKey oss上的文件名
*/
public String getNoExpireUrl(String fileKey) {
OSS ossClient = getOssClient();
GeneratePresignedUrlRequest generatePresignedUrlRequest = new GeneratePresignedUrlRequest(ossProperties.getBucketName(), fileKey, HttpMethod.GET);
URL url = ossClient.generatePresignedUrl(generatePresignedUrlRequest);
return url.toString();
}
/**
* 删除某个文件
* @param fileKey oss上的文件名
*/
public void removeObject(String fileKey) {
OSS ossClient = getOssClient();
ossClient.deleteObject(ossProperties.getBucketName(), fileKey);
}
/**
* 获取某个文件
* @param fileKey oss上的文件名
* @return
*/
public InputStream getObject(String fileKey) {
OSS ossClient = getOssClient();
OSSObject object = ossClient.getObject(ossProperties.getBucketName(), fileKey);
return object.getObjectContent();
}
}
文件格式白名单及分类
一个常量类:
/**
* @author 29443
* @version 1.0
* @date 2022/4/25
* 确定要上传的文件的目录,也可作上传白名单
*/
public class MediaConstants {
public static List<String> IMG_SUFFIXS = Arrays.asList("jpg","png","jpeg");
public static List<String> VIDEO_SUFFIXS = Arrays.asList("mp4","avi","rmvb","mov");
public static List<String> RADIO_SUFFIXS = Arrays.asList("mp3","wma","wav","mpeg-4","cd","m4a");
}
判断分类(一个工具类):
/**
* @author 29443
* @version 1.0
* @date 2022/4/25
*/
public class FileUtil {
/**
* 获取文件后缀
* @param fileName
* @return
*/
public static String getSuffix(String fileName) {
String suffix = fileName.substring(fileName.lastIndexOf('.') + 1);
return suffix;
}
/**
* 获取该文件上传到oss哪个目录
* @param suffix 文件后缀
* @return
*/
public static String getFileType(String suffix) {
String fileType = "";
if (MediaConstants.IMG_SUFFIXS.contains(suffix)) {
fileType = FileTypeEnum.IMAGE.getType();
}
else if (MediaConstants.VIDEO_SUFFIXS.contains(suffix)) {
fileType = FileTypeEnum.VIDEO.getType();
}
else if (MediaConstants.RADIO_SUFFIXS.contains(suffix)) {
fileType = FileTypeEnum.RADIO.getType();
}
if (StringUtils.isBlank(fileType)) {
throw new FileFormatNotSupport("文件格式" + suffix + "不支持", HttpStatusConstants.ERROR);
}
return fileType;
}
/**
* 获取文件的信息
* @param file
* @return
*/
public static FileInfo getFileInfo(MultipartFile file) {
String originalFilename = file.getOriginalFilename();
String random = UUID.randomUUID().toString();
String suffix = getSuffix(originalFilename);
String fileType = getFileType(suffix);
String fileKey = fileType + "/" + random;
FileInfo fileInfo = new FileInfo();
fileInfo.setFileKey(fileKey);
fileInfo.setContentType(file.getContentType());
fileInfo.setSize(file.getSize());
return fileInfo;
}
}
FileInfo类:(用于将文件信息存入数据库)
@Data
@TableName("sys_file")
@ApiModel(value = "文件对象", description = "文件对象")
public class FileInfo {
@TableId(value = "id", type = IdType.AUTO)
private String id;
@ApiModelProperty("oss文件名")
@TableField("file_key")
private String fileKey;
@ApiModelProperty("文件格式")
@TableField("content_type")
private String contentType;
@ApiModelProperty("文件大小")
@TableField("size")
private Long size;
@ApiModelProperty("文件原始名")
@TableField("original_filename")
private String originalFilename;
@ApiModelProperty("创建时间")
@TableField(value = "gmt_create", fill = FieldFill.INSERT)
private LocalDateTime gmtCreate;
@ApiModelProperty("更新时间")
@TableField(value = "gmt_update", fill = FieldFill.UPDATE)
private LocalDateTime gmtUpdate;
@TableField("is_deleted")
private Integer isDeleted;
}
service层调用OssTemplate完成文件操作
这里就是controller调用service的操作。
我这里就放一个上传文件的demo吧:
Controller:
/**
* 多文件上传
* @param files
* @return
*/
@PostMapping("/upload")
public ResponseData uploads(@RequestPart("files") @NotEmpty List<MultipartFile> files) {
return ossFileService.putObjects(files);
}
Service:
@Override
@Transactional(rollbackFor = Exception.class)
public ResponseData putObjects(List<MultipartFile> fileList) {
int successCount = 0;
int fileNumber = fileList.size();
List<String> urls = new ArrayList();
for (MultipartFile file : fileList) {
FileInfo fileInfo = FileUtil.getFileInfo(file);
try {
ossTemplate.putObject(file.getInputStream(), fileInfo);
fileMapper.insert(fileInfo);
urls.add(ossTemplate.getHasExpireUrl(fileInfo.getFileKey()));
successCount++;
} catch (IOException e) {
e.printStackTrace();
}
}
String msg = String.format("上传文件总数:'{}',成功'{}'个,失败'{}'个",
fileNumber,
successCount,
fileNumber-successCount);
return ResponseData.success(msg, urls);
}
至此,完毕。