ruoyi-OSS阿里云
ruoyi-OSS阿里云
ruoyi-OSS阿里云
1.先去阿里云开通OSS https://blog.csdn.net/m0_55155505/article/details/123688022
2.导入依赖,ruoyi默认配置
<!--阿里云sms短信服务-->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>4.4.6</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-dysmsapi</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.15.0</version>
</dependency>
3.配置文件
aliyun:
oss:
file:
private:
keyid:
keysecret:
bucketname:
endpoint:
domain:
ramkeyid: ramkeyid
ramkeysecret:
ramarn:ramarn
open:
keyid:
keysecret:
bucketname:
endpoint:
domain:
4.文件结构

5.cloud文件夹
AliyunCloudStorageService
package com.ruoyi.oss.cloud;
import cn.hutool.json.JSONUtil;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.OSSObject;
import com.ruoyi.oss.config.OssOpenProperies;
import com.ruoyi.oss.config.OssPrivateProperies;
import com.ruoyi.oss.vo.request.SysOssDownloadFileRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
/**
* 阿里云存储
*
* @author
*/
public class AliyunCloudStorageService extends CloudStorageService {
private static final Logger logger = LoggerFactory.getLogger(AliyunCloudStorageService.class);
private OSS client;
/**
* 文件是否公开参数
*/
private Boolean open;
public AliyunCloudStorageService(){
//初始化
init(true);
}
public AliyunCloudStorageService(Boolean isOpen){
//初始化
init(isOpen);
}
private void init(Boolean isOpen){
if (isOpen == null ? true : isOpen) {
client = new OSSClientBuilder().build(OssOpenProperies.END_POINT, OssOpenProperies.ACCESS_KEY_ID, OssOpenProperies.ACCESS_KEY_SECRET);
open = true;
} else {
client = new OSSClientBuilder().build(OssPrivateProperies.END_POINT, OssPrivateProperies.ACCESS_KEY_ID, OssPrivateProperies.ACCESS_KEY_SECRET);
open = false;
}
}
@Override
public String upload(InputStream inputStream, String path) {
try {
if (open) {
client.putObject(OssOpenProperies.BUCKET_NAME, path, inputStream);
} else {
client.putObject(OssPrivateProperies.BUCKET_NAME, path, inputStream);
}
//就是这个 => https://Domain/path
if (open) {
return OssOpenProperies.DOMAIN + "/" + path;
}
return OssPrivateProperies.DOMAIN + "/" + path;
} catch (Exception e){
logger.error("上传文件失败,请检查配置信息", e);
return null;
} finally {
if (client != null) {
client.shutdown();
}
}
}
@Override
public void download(SysOssDownloadFileRequest req, HttpServletResponse response) {
BufferedInputStream bis = null;
OutputStream out = null;
try {
// ossObject包含文件所在的存储空间名称、文件名称、文件元信息以及一个输入流。
OSSObject ossObject;
if (open) {
ossObject = client.getObject(OssOpenProperies.BUCKET_NAME, req.getPath());
} else {
ossObject = client.getObject(OssPrivateProperies.BUCKET_NAME, req.getPath());
}
out = response.getOutputStream();
response.setContentType("application/octet-stream");
response.setHeader(HttpHeaders.CONTENT_TYPE, "application/octet-stream");
String encode = URLEncoder.encode(req.getOldName(), "utf-8");
// 设置文件名
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + encode);
// 读取文件内容。
bis = new BufferedInputStream(ossObject.getObjectContent());
byte[] buffer = new byte[1024];
int i = bis.read(buffer);
while (i != -1) {
out.write(buffer, 0, i);
i = bis.read(buffer);
}
} catch (Exception e){
logger.error("下载文件失败,请检查配置信息" + JSONUtil.toJsonStr(req), e);
throw new RuntimeException("下载文件失败,请检查配置信息");
} finally {
if (client != null) {
client.shutdown();
}
try {
if (bis != null) {
bis.close();
}
if (out != null) {
out.close();
}
} catch (IOException e) {
logger.error("通道关闭异常", e);
}
}
}
}
CloudStorageService
package com.ruoyi.oss.cloud;
import cn.hutool.core.date.DatePattern;
import com.ruoyi.oss.vo.request.SysOssDownloadFileRequest;
import org.apache.commons.lang3.StringUtils;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.util.Date;
import java.util.UUID;
/**
* OSS
*/
public abstract class CloudStorageService {
/**
* 文件路径
* @param prefix 前缀
* @param suffix 后缀
* @return 返回上传路径
*/
public String getPath(String prefix, String suffix) {
//生成uuid
String uuid = UUID.randomUUID().toString().replaceAll("-", "");
//文件路径
String path = DatePattern.PURE_DATE_FORMAT.format(new Date()) + "/" + uuid;
if(StringUtils.isNotBlank(prefix)){
path = prefix + "/" + path;
}
return path + suffix;
}
/**
* 文件上传
* @param inputStream 字节流
* @param path 文件路径,包含文件名
* @return 返回http地址
*/
public abstract String upload(InputStream inputStream, String path);
/**
* 文件下载
* @param req 文件路径,包含文件名
* @return 返回http地址
*/
public abstract void download(SysOssDownloadFileRequest req, HttpServletResponse response);
}
OSSFactory
package com.ruoyi.oss.cloud;
/**
* 文件上传Factory
*/
public final class OSSFactory {
public static CloudStorageService build(){
//后面连接其它云可以在这里写逻辑切换
return new AliyunCloudStorageService();
}
public static CloudStorageService build(Boolean isOpen){
//后面连接其它云可以在这里写逻辑切换
return new AliyunCloudStorageService(isOpen);
}
}
OssSignHeader
package com.ruoyi.oss.cloud;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;
import java.util.TimeZone;
public class OssSignHeader {
private static final String HMAC_SHA1_ALGORITHM = "HmacSHA1";
private final static String ALGORITHM = "HmacSHA1";
public static String hmacSha1(String data, String key) {
try {
Mac mac = Mac.getInstance(HMAC_SHA1_ALGORITHM);
SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), ALGORITHM);
mac.init(keySpec);
byte[] rawHmac;
rawHmac = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
return new String(Base64.encodeBase64(rawHmac));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static String buildSignData(String date, String VERB, String canonicalizedResource){
return VERB + "\n"+
"application/octet-stream"+ "\n"
+ date + "\n"
+ canonicalizedResource;
}
public static String getGMTDate(){
Calendar cd = Calendar.getInstance();
SimpleDateFormat sdf = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US);
sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
return sdf.format(cd.getTime());
}
}
6.config文件夹
OssOpenProperies
package com.ruoyi.oss.config;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class OssOpenProperies implements InitializingBean {
//oss
@Value("${aliyun.oss.file.open.keyid}")
private String keyId;
@Value("${aliyun.oss.file.open.keysecret}")
private String keySecret;
@Value("${aliyun.oss.file.open.bucketname}")
private String bucketName;
@Value("${aliyun.oss.file.open.endpoint}")
private String endpoint;
@Value("${aliyun.oss.file.open.domain}")
private String domain;
public static String ACCESS_KEY_ID;
public static String ACCESS_KEY_SECRET;
public static String BUCKET_NAME;
public static String END_POINT;
public static String DOMAIN;
@Override
public void afterPropertiesSet() throws Exception {
ACCESS_KEY_ID = keyId;
ACCESS_KEY_SECRET = keySecret;
BUCKET_NAME = bucketName;
END_POINT = endpoint;
DOMAIN = domain;
}
}
OssPrivateProperies
package com.ruoyi.oss.config;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class OssPrivateProperies implements InitializingBean {
//oss
@Value("${aliyun.oss.file.private.keyid}")
private String keyId;
@Value("${aliyun.oss.file.private.keysecret}")
private String keySecret;
@Value("${aliyun.oss.file.private.bucketname}")
private String bucketName;
@Value("${aliyun.oss.file.private.endpoint}")
private String endpoint;
@Value("${aliyun.oss.file.private.domain}")
private String domain;
@Value("${aliyun.oss.file.private.ramkeyid}")
private String ramKeyId;
@Value("${aliyun.oss.file.private.ramkeysecret}")
private String ramKeySecret;
@Value("${aliyun.oss.file.private.ramarn}")
private String ramArn;
public static String ACCESS_KEY_ID;
public static String ACCESS_KEY_SECRET;
public static String BUCKET_NAME;
public static String END_POINT;
public static String DOMAIN;
public static String RAM_KEY_ID;
public static String RAM_KEY_SECRET;
public static String RAM_ARN;
@Override
public void afterPropertiesSet() throws Exception {
ACCESS_KEY_ID = keyId;
ACCESS_KEY_SECRET = keySecret;
BUCKET_NAME = bucketName;
END_POINT = endpoint;
DOMAIN = domain;
RAM_KEY_ID = ramKeyId;
RAM_KEY_SECRET = ramKeySecret;
RAM_ARN = ramArn;
}
}
7.entity文件夹
package com.ruoyi.oss.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
/**
* 文件上传表(SysFile) 实体类
**/
@Data
@TableName("sys_file")
public class SysFileModel {
@TableId(type = IdType.AUTO)
private Long id;
/** 文件业务来源编码 */
private String bizKey;
/** 原文件名 */
private String oldName;
/** 新文件名(文件储存在服务器的名称,唯一) */
private String newName;
/** 文件大小(单位:字节) */
private Long size;
/** 文件储存在oss的路径 */
private String path;
/** 文件类型 */
private String type;
/** 访问oss的url */
private String url;
/** 是否公开 10:是 公开的文件 -10:否 私有的文件 */
private Integer open;
}
8.enums文件夹,根据业务需求
package com.ruoyi.oss.enums;
import lombok.Getter;
import lombok.Setter;
/**
* 文件业务来源枚举
*/
public enum BizKeyTypeEnum {
/**
* 正常
*/
ACCOUNT("account", "账号文件上传业务编码"),
/**
* 正常
*/
APPLETS("applets", "小程序配置");
@Getter
@Setter
private String code;
@Getter
@Setter
private String msg;
BizKeyTypeEnum(String code, String msg) {
this.code = code;
this.msg = msg;
}
/**
* 判断文件业务是否合法
* @param code
* @return
*/
public static boolean getCodeExist(String code) {
for (BizKeyTypeEnum value : values()) {
if (value.getCode().equals(code)) {
return true;
}
}
return false;
}
}
package com.ruoyi.oss.enums;
import lombok.Getter;
import lombok.Setter;
/**
* 文件公开类型枚举
*/
public enum FileOpenTypeEnum {
/**
* 公开
*/
OPEN("open", 10),
/**
* 私有
*/
PRIVATE("private", -10);
@Getter
@Setter
private String code;
@Getter
@Setter
private Integer num;
FileOpenTypeEnum(String code, Integer num) {
this.code = code;
this.num = num;
}
public static boolean isOpen(Integer num) {
return num.intValue() == OPEN.getNum().intValue();
}
}
9.Impl
package com.ruoyi.oss.service.impl;/**
* 文件上传表(SysFile)Service实现类
**/
@Service
@Transactional(rollbackFor = Exception.class)
public class SysFileServiceImpl extends ServiceImpl<SysFileMapper, SysFileModel> implements SysFileService {
private static final Logger logger = LoggerFactory.getLogger(SysFileServiceImpl.class);
@Override
public Result ossUpload(MultipartFile file, String bizKey, Integer open) {
try {
//获取文件名
String originalFilename = file.getOriginalFilename();
if (StrUtil.isBlank(originalFilename)) {
return Result.failed("文件名为空!");
}
String uuidFileName = FileNameUtil.getUUIDFileName();
String fileType = FileNameUtil.getFileType(originalFilename);
String newFileName = uuidFileName + fileType;
// 上传文件流
InputStream inputStream = file.getInputStream();
Calendar c = Calendar.getInstance();
int year = c.get(Calendar.YEAR);
int month = c.get(Calendar.MONTH) + 1;
int date = c.get(Calendar.DATE);
//根据时间拼接path
String path = year+"/"+month+"/"+date+"/"+newFileName;
//上传
String domain = OSSFactory.build(FileOpenTypeEnum.isOpen(open)).upload(inputStream, path);
if (StrUtil.isBlank(domain)) {
return Result.failed("文件上传失败!");
}
//文件上传成功后保存到数据库表
long size = file.getSize();
SysFileModel sysFileModel = new SysFileModel();
sysFileModel.setBizKey(bizKey);
sysFileModel.setNewName(newFileName);
sysFileModel.setOldName(originalFilename);
sysFileModel.setUrl(domain);
sysFileModel.setPath(path);
sysFileModel.setSize(size);
sysFileModel.setType(fileType);
sysFileModel.setOpen(open);
save(sysFileModel);
//返回文件上传响应体
SysFileResponse response = new SysFileResponse();
response.setNewName(sysFileModel.getNewName());
response.setOldName(sysFileModel.getOldName());
response.setPath(sysFileModel.getPath());
response.setUrl(domain);
response.setOpen(open);
response.setSize(size);
return Result.success(response);
} catch (Exception e) {
logger.error("文件上传oss异常!", e);
return Result.failed("文件上传失败!");
}
}
/**
* 获取授权stsUrl
* @param req
* @return
*/
@Override
public Result getSTSUrl(SysStsUrlRequest req){
//有效时间1小时
Long overtime = 3600L;
//当前时间
Long time = System.currentTimeMillis() / 1000;
//生成指定参数的签名URL
//生成OSSClient
OSS ossClient = new OSSClientBuilder().build(OssPrivateProperies.END_POINT, OssPrivateProperies.ACCESS_KEY_ID, OssPrivateProperies.ACCESS_KEY_SECRET);
// 创建请求。
GeneratePresignedUrlRequest generatePresignedUrlRequest = new GeneratePresignedUrlRequest(OssPrivateProperies.BUCKET_NAME, req.getPath());
generatePresignedUrlRequest.setMethod(HttpMethod.GET);
//设置响应头强制下载
ResponseHeaderOverrides responseHeaders = new ResponseHeaderOverrides();
responseHeaders.setContentDisposition("attachment;");
generatePresignedUrlRequest.setResponseHeaders(responseHeaders);
// 设置URL过期时间为1小时。
Date expiration = new Date(System.currentTimeMillis() + overtime * time );
generatePresignedUrlRequest.setExpiration(expiration);
// 生成签名URL。
URL url = ossClient.generatePresignedUrl(generatePresignedUrlRequest);
// 获取STS
// String ramSecurityToken = getRamSecurityToken();
// String stsUrl = url.toString() + "&Signature=" + ramSecurityToken;
return Result.success(url);
}
@Override
public void ossDownload(SysOssDownloadFileRequest req, HttpServletResponse response) {
OSSFactory.build(FileOpenTypeEnum.isOpen(req.getOpen())).download(req, response);
}
//获取临时访问url
@Override
public Result getAutoUrl(List<String> reqList) {
try {
// Endpoint以杭州为例,其它Region请按实际情况填写。
String endpoint = OssPrivateProperies.END_POINT;
String accessKeyId = OssPrivateProperies.ACCESS_KEY_ID;
String accessKeySecret = OssPrivateProperies.ACCESS_KEY_SECRET;
String bucketName = OssPrivateProperies.BUCKET_NAME;
String domain = OssPrivateProperies.DOMAIN + "/";
List<URL> urlList = new ArrayList<>();
// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
// 设置URL过期时间为1小时。
Date expiration = new Date(new Date().getTime() + 3600 * 1000);
for(String str : reqList) {
if (StrUtil.isNotEmpty(str)) {
if (str.contains(domain)) {
String objectName = str.replace(domain, "");
// 生成以GET方法访问的签名URL,访客可以直接通过浏览器访问相关内容。
URL url = ossClient.generatePresignedUrl(bucketName, objectName, expiration);
urlList.add(url);
continue;
}
}
return Result.failed("url错误");
}
// 关闭OSSClient。
ossClient.shutdown();
return Result.success(urlList);
} catch (Exception e) {
logger.error("获取临时地址失败!", e);
return Result.failed();
}
}
/**
* 获取RAM权限角色的STS
* @return
*/
private String getRamSecurityToken(){
// STS接入地址,例如sts.cn-hangzhou.aliyuncs.com。
String endpoint = OssPrivateProperies.END_POINT;
String accessKeyId = OssPrivateProperies.RAM_KEY_ID;
String accessKeySecret = OssPrivateProperies.RAM_KEY_SECRET;
// 获取的角色ARN。
String roleArn = OssPrivateProperies.RAM_ARN;
// 自定义角色会话名称,用来区分不同的令牌,例如可填写为SessionTest。
String roleSessionName = Convert.toStr(SecurityUtils.getLoginUser().getUserId());
// 临时访问凭证最后获得的权限是RAM角色权限和该Policy设置权限的交集,即仅允许将文件上传至目标存储空间examplebucket下的exampledir目录。
//如果确定该policy不变,可以抽离出来
String policy = new StringBuffer()
.append("{")
.append(" \"Version\": \"1\", ")
.append(" \"Statement\": [")
.append(" {")
.append(" \"Action\": [")
.append(" \"oss:GetObject\"")
.append(" ], ")
//TODO 特别注意这里的ARN:bucket格式 是否有“:”号?
.append(" \"Resource\": [").append("\"").append(OssPrivateProperies.RAM_ARN).append(":").append(OssPrivateProperies.BUCKET_NAME).append("/*\" ").append("], ")
.append(" \"Effect\": \"Allow\"")
.append(" }")
.append(" ]")
.append("}")
.toString();
try {
// regionId表示RAM的地域ID。以华东1(杭州)地域为例,regionID填写为cn-hangzhou。也可以保留默认值,默认值为空字符串("")。
String regionId = "";
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();
request.setMethod(MethodType.POST);
request.setRoleArn(roleArn);
request.setRoleSessionName(roleSessionName);
// 如果policy为空,则用户将获得该角色下所有权限。
request.setPolicy(policy);
// 设置临时访问凭证的有效时间为3600秒。
request.setDurationSeconds(3600L);
final AssumeRoleResponse response = client.getAcsResponse(request);
return response.getCredentials().getSecurityToken();
} catch (ClientException e) {
logger.error("获取临时访问权限异常!Error code:" + e.getErrCode() + ",Error message: " + e.getErrMsg() + ",RequestId: " + e.getRequestId(), e);
return "";
}
}
}
10.utils
package com.ruoyi.oss.utils;
import java.util.UUID;
public class FileNameUtil {
//根据UUID生成文件名
public static String getUUIDFileName() {
UUID uuid = UUID.randomUUID();
return uuid.toString().replace("-", "");
}
//根据给定的文件名和后缀截取文件名
public static String getFileType(String fileName){
//9527s.jpg
int index = fileName.lastIndexOf(".");
return fileName.substring(index);
}
}
11.vo
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class SysOssDownloadFileRequest {
//文件下载请求数据实体
@ApiModelProperty(value = "文件在服务器的名称")
@NotBlank(message = "文件在服务器的名称不能为空!")
private String newName;
@ApiModelProperty(value = "原文件名称")
@NotBlank(message = "原文件名称不能为空!")
private String oldName;
@ApiModelProperty(value = "文件在服务器的路径")
@NotBlank(message = "文件在服务器的路径不能为空!")
private String path;
@ApiModelProperty(value = "文件是否公开")
@NotNull(message = "文件是否公开不能为空!")
private Integer open;
}
/**
* 用于获取stsUrl的请求体
*/
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class SysStsUrlRequest {
@ApiModelProperty(value = "url")
private String url;
@ApiModelProperty(value = "newName")
private String newName;
@ApiModelProperty(value = "path")
private String path;
}
/**
* 文件上传返回数据实体
*
*/
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class SysFileResponse {
@ApiModelProperty(value = "原文件名称", required = true)
private String oldName;
@ApiModelProperty(value = "文件在服务器的名称", required = true)
private String newName;
@ApiModelProperty(value = "文件在服务器的路径", required = true)
private String path;
@ApiModelProperty(value = "文件在访问路径", required = true)
private String url;
@ApiModelProperty(value = "文件是否公开 10:是 公开的文件 -10:否 私有的文件", required = true)
private Integer open;
@ApiModelProperty(value = "文件大小", required = true)
private Long size;
}
12.Controller
@ApiOperation(value = "单个文件上传接口", notes = "单个文件上传接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "bizKey", value = "业务编码",required = true, paramType = "path", dataType = "String"),
@ApiImplicitParam(name = "open", value = "是否公开 10:公开 -10:私有",required = true, paramType = "path", dataType = "Integer")
})
@PostMapping("/ossUpload/{bizKey}/{open}")
public Result ossUpload(@RequestPart("file") MultipartFile file, @PathVariable("bizKey") String bizKey, @PathVariable("open") Integer open, HttpServletRequest request) {
if (Md5Utils.signatureCheck2(request)) {
if(file.isEmpty()) {
return Result.failed("文件不存在!");
}
if (StrUtil.isBlank(bizKey)) {
return Result.failed("文件业务编码不能为空!");
}
//返回文件上传路径
return sysFileService.ossUpload(file, bizKey, open);
}
return Result.failed("非法请求");
}
@ApiOperation(value = "oss单个文件下载接口", notes = "oss单个文件下载接口")
@PostMapping("/ossDownload")
public void ossDownload(@RequestBody SysOssDownloadFileRequest req, HttpServletResponse response) {
sysFileService.ossDownload(req, response);
}
/**
* 获取授权stsUrl
*
**/
@ApiOperation(value = "获取授权stsUrl", notes = "获取授权stsUrl")
@PostMapping("/getSTSUrl")
public Result getSTSUrl(@RequestBody SysStsUrlRequest req) {
//返回文件stsUrl
return sysFileService.getSTSUrl(req);
}
/**
* 获取签名临时访问url
*/
@ApiOperation(value = "获取签名临时访问url", notes = "获取签名临时访问url")
@PostMapping("/getAutoUrl")
public Result getAutoUrl(@RequestBody List<String> reqList){
//返回文件签名Url
return sysFileService.getAutoUrl(reqList);
}

浙公网安备 33010602011771号