OSS相关方法

pull/1121/head
梁宇奇 2025-03-25 19:12:03 +08:00
parent 8123cde981
commit 1982fc9889
11 changed files with 517 additions and 152 deletions

View File

@ -37,6 +37,8 @@
<logback.version>1.2.13</logback.version>
<spring-security.version>5.7.12</spring-security.version>
<spring-framework.version>5.3.39</spring-framework.version>
<!-- aliyun -->
<aliyun-sdk-oss.version>3.17.4</aliyun-sdk-oss.version>
</properties>
<!-- 依赖声明 -->
@ -233,6 +235,12 @@
<version>${hutool.version}</version>
</dependency>
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>${aliyun-sdk-oss.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

View File

@ -1,163 +1,47 @@
package com.ruoyi.web.controller.common;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.aliyun.oss.common.auth.Credentials;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.framework.config.properties.OSSProperties;
import com.ruoyi.framework.oss.OSSClientWrapper;
import com.ruoyi.web.controller.common.vo.STSCredentialsVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.file.FileUploadUtils;
import com.ruoyi.common.utils.file.FileUtils;
import com.ruoyi.framework.config.ServerConfig;
/**
*
*
*
* @author ruoyi
*/
@Api(tags = "通用")
@Slf4j
@RestController
@RequestMapping("/common")
public class CommonController
{
private static final Logger log = LoggerFactory.getLogger(CommonController.class);
public class CommonController {
@Autowired
private ServerConfig serverConfig;
private OSSClientWrapper ossClient;
@Autowired
private OSSProperties ossProperties;
private static final String FILE_DELIMETER = ",";
/**
*
*
* @param fileName
* @param delete
*/
@GetMapping("/download")
public void fileDownload(String fileName, Boolean delete, HttpServletResponse response, HttpServletRequest request)
{
try
{
if (!FileUtils.checkAllowDownload(fileName))
{
throw new Exception(StringUtils.format("文件名称({})非法,不允许下载。 ", fileName));
}
String realFileName = System.currentTimeMillis() + fileName.substring(fileName.indexOf("_") + 1);
String filePath = RuoYiConfig.getDownloadPath() + fileName;
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
FileUtils.setAttachmentResponseHeader(response, realFileName);
FileUtils.writeBytes(filePath, response.getOutputStream());
if (delete)
{
FileUtils.deleteFile(filePath);
}
}
catch (Exception e)
{
log.error("下载文件失败", e);
}
@ApiOperation("获取OSS临时访问凭证")
@GetMapping("/getCredentials")
public R<STSCredentialsVO> getCredentials() {
STSCredentialsVO vo = new STSCredentialsVO();
Credentials credentials = ossClient.createStsCredentials();
vo.setAccessKeyId(credentials.getAccessKeyId());
vo.setAccessKeySecret(credentials.getSecretAccessKey());
vo.setSecurityToken(credentials.getSecurityToken());
vo.setBucketName(ossProperties.getBucketName());
vo.setRegionId(ossProperties.getRegionId());
vo.setEndPoint(ossProperties.getEndPoint());
vo.setExpiredDuration(ossProperties.getExpiredDuration());
vo.setHttpsFlag(ossProperties.isHttps());
return R.ok(vo);
}
/**
*
*/
@PostMapping("/upload")
public AjaxResult uploadFile(MultipartFile file) throws Exception
{
try
{
// 上传文件路径
String filePath = RuoYiConfig.getUploadPath();
// 上传并返回新文件名称
String fileName = FileUploadUtils.upload(filePath, file);
String url = serverConfig.getUrl() + fileName;
AjaxResult ajax = AjaxResult.success();
ajax.put("url", url);
ajax.put("fileName", fileName);
ajax.put("newFileName", FileUtils.getName(fileName));
ajax.put("originalFilename", file.getOriginalFilename());
return ajax;
}
catch (Exception e)
{
return AjaxResult.error(e.getMessage());
}
}
/**
*
*/
@PostMapping("/uploads")
public AjaxResult uploadFiles(List<MultipartFile> files) throws Exception
{
try
{
// 上传文件路径
String filePath = RuoYiConfig.getUploadPath();
List<String> urls = new ArrayList<String>();
List<String> fileNames = new ArrayList<String>();
List<String> newFileNames = new ArrayList<String>();
List<String> originalFilenames = new ArrayList<String>();
for (MultipartFile file : files)
{
// 上传并返回新文件名称
String fileName = FileUploadUtils.upload(filePath, file);
String url = serverConfig.getUrl() + fileName;
urls.add(url);
fileNames.add(fileName);
newFileNames.add(FileUtils.getName(fileName));
originalFilenames.add(file.getOriginalFilename());
}
AjaxResult ajax = AjaxResult.success();
ajax.put("urls", StringUtils.join(urls, FILE_DELIMETER));
ajax.put("fileNames", StringUtils.join(fileNames, FILE_DELIMETER));
ajax.put("newFileNames", StringUtils.join(newFileNames, FILE_DELIMETER));
ajax.put("originalFilenames", StringUtils.join(originalFilenames, FILE_DELIMETER));
return ajax;
}
catch (Exception e)
{
return AjaxResult.error(e.getMessage());
}
}
/**
*
*/
@GetMapping("/download/resource")
public void resourceDownload(String resource, HttpServletRequest request, HttpServletResponse response)
throws Exception
{
try
{
if (!FileUtils.checkAllowDownload(resource))
{
throw new Exception(StringUtils.format("资源文件({})非法,不允许下载。 ", resource));
}
// 本地资源路径
String localPath = RuoYiConfig.getProfile();
// 数据库资源地址
String downloadPath = localPath + StringUtils.substringAfter(resource, Constants.RESOURCE_PREFIX);
// 下载名称
String downloadName = StringUtils.substringAfterLast(downloadPath, "/");
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
FileUtils.setAttachmentResponseHeader(response, downloadName);
FileUtils.writeBytes(downloadPath, response.getOutputStream());
}
catch (Exception e)
{
log.error("下载文件失败", e);
}
}
}

View File

@ -0,0 +1,58 @@
package com.ruoyi.web.controller.common.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
/**
* @author liangyuqi
* @date 2021/11/15 14:36
*/
@ApiModel
@Getter
@Setter
@ToString
public class STSCredentialsVO {
/**
* ID
*/
@ApiModelProperty("ID")
private String accessKeyId;
/**
* Secret
*/
@ApiModelProperty("Secret")
private String accessKeySecret;
/**
* token
*/
@ApiModelProperty("token")
private String securityToken;
/**
*
*/
@ApiModelProperty("凭证的有效时长,单位秒")
private Long expiredDuration;
/**
* regionId
*/
@ApiModelProperty("regionId")
private String regionId;
/**
* endPoint
*/
@ApiModelProperty("endPoint")
private String endPoint;
/**
*
*/
@ApiModelProperty("bucketName")
private String bucketName;
/**
* https
*/
@ApiModelProperty("https标记")
private Boolean httpsFlag;
}

View File

@ -12,6 +12,15 @@ ruoyi:
addressEnabled: false
# 验证码类型 math 数字计算 char 字符验证
captchaType: math
oss:
endPoint: oss-cn-beijing.aliyuncs.com
accessKeyId: LTAI5tFXTfY5Rsiwvrg9gUuk
accessKeySecret: Ebbj7anXSdbJwfm5zbTfHkJ5QWDbTP
bucketName: lyq-private
https: true
regionId: cn-beijing
roleArn: acs:ram::1919425406190533:role/sts-role
expiredDuration: 3600
# 开发环境配置
server:

View File

@ -19,7 +19,6 @@
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
@ -27,6 +26,12 @@
<artifactId>hutool-all</artifactId>
</dependency>
<!-- oss -->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
</dependency>
<!-- mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>

View File

@ -2,12 +2,14 @@ package com.ruoyi.common.core.domain;
import java.io.Serializable;
import com.ruoyi.common.constant.HttpStatus;
import lombok.ToString;
/**
*
*
* @author ruoyi
*/
@ToString
public class R<T> implements Serializable
{
private static final long serialVersionUID = 1L;

View File

@ -0,0 +1,42 @@
package com.ruoyi.framework.config;
import com.ruoyi.framework.config.properties.OSSProperties;
import com.ruoyi.framework.oss.OSSClientWrapper;
import com.ruoyi.framework.oss.OSSConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author liangyq
* @date 2025-03-24
*/
@Configuration
@ConditionalOnClass(OSSClientWrapper.class)
@ConditionalOnProperty({"oss.accessKeyId", "oss.accessKeySecret", "oss.endPoint", "oss.bucketName"})
@EnableConfigurationProperties(OSSProperties.class)
public class OSSAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public OSSClientWrapper ossClient(OSSProperties properties) {
OSSConfiguration ossConfiguration = toOSSConfiguration(properties);
return new OSSClientWrapper(ossConfiguration);
}
private OSSConfiguration toOSSConfiguration(OSSProperties properties) {
OSSConfiguration configuration = new OSSConfiguration();
configuration.setEndpoint(properties.getEndPoint());
configuration.setAccessKeyId(properties.getAccessKeyId());
configuration.setAccessKeySecret(properties.getAccessKeySecret());
configuration.setBucketName(properties.getBucketName());
configuration.setRegionId(properties.getRegionId());
configuration.setRoleArn(properties.getRoleArn());
configuration.setHttps(properties.isHttps());
configuration.setExpiredDuration(properties.getExpiredDuration());
return configuration;
}
}

View File

@ -0,0 +1,29 @@
package com.ruoyi.framework.config.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @author liangyq
* @date 2025-03-24
*/
@Data
@ConfigurationProperties(prefix = "oss")
public class OSSProperties {
private String endPoint;
private String accessKeyId;
private String accessKeySecret;
private String bucketName;
private boolean https;
private String regionId;
private String roleArn;
private Long expiredDuration;
}

View File

@ -0,0 +1,301 @@
package com.ruoyi.framework.oss;
import com.aliyun.oss.*;
import com.aliyun.oss.common.auth.Credentials;
import com.aliyun.oss.common.auth.CredentialsProviderFactory;
import com.aliyun.oss.common.auth.STSAssumeRoleSessionCredentialsProvider;
import com.aliyun.oss.common.comm.Protocol;
import com.aliyun.oss.model.*;
import io.jsonwebtoken.lang.Assert;
import lombok.extern.slf4j.Slf4j;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.net.URL;
import java.util.Base64;
import java.util.Date;
/**
* @author liangyq
* @date 2025-03-24
*/
@Slf4j
public class OSSClientWrapper {
private final OSS client;
private final OSSConfiguration configuration;
public OSSClientWrapper(OSSConfiguration configuration) {
this.configuration = configuration;
ClientBuilderConfiguration config = getClientBuilderConfiguration();
client = new OSSClientBuilder().build(configuration.getEndpoint(), configuration.getAccessKeyId(), configuration.getAccessKeySecret(), config);
}
public OSSClientWrapper(STSAssumeRoleSessionCredentialsProvider credentialsProvider, OSSConfiguration configuration) {
this.configuration = configuration;
ClientBuilderConfiguration config = getClientBuilderConfiguration();
client = new OSSClientBuilder().build(configuration.getEndpoint(), credentialsProvider, config);
}
/**
* 访
* 访OSSConfiguration#expiredDuration
*
* @return 访
*/
public OSSClientWrapper createSTSClient() {
STSAssumeRoleSessionCredentialsProvider credentialsProvider = this.createSTSCredentialsProvider();
Credentials credentials = credentialsProvider.getCredentials();
OSSConfiguration newOSSConfiguration = this.configuration.clone();
newOSSConfiguration.setAccessKeyId(credentials.getAccessKeyId());
newOSSConfiguration.setAccessKeySecret(credentials.getSecretAccessKey());
return new OSSClientWrapper(credentialsProvider, newOSSConfiguration);
}
/**
* 访key
* 访OSSConfiguration#expiredDuration
*
* @return 访key
*/
public Credentials createStsCredentials() {
return this.createSTSCredentialsProvider().getCredentials();
}
/**
*
*
* @param key key
* @param inputStream
*/
public void upload(String key, InputStream inputStream)
throws Exception {
upload(key, inputStream, null);
}
/**
*
*
* @param key key
* @param inputStream
* @param contentType
*/
public void upload(String key, InputStream inputStream, String contentType)
throws Exception {
upload(key, inputStream, contentType, null);
}
/**
*
*
* @param key key
* @param inputStream
* @param contentType
* @param contentDisposition
*/
public void upload(String key, InputStream inputStream, String contentType, String contentDisposition) throws Exception {
ObjectMetadata objectMeta = new ObjectMetadata();
objectMeta.setContentLength(inputStream.available());
// 可以在metadata中标记文件类型
if (contentType == null) {
// contentType = deduceContentType(key);
}
objectMeta.setContentType(contentType);
// 设置下载名
objectMeta.setContentDisposition(contentDisposition);
client.putObject(configuration.getBucketName(), key, inputStream, objectMeta);
if (!client.doesObjectExist(configuration.getBucketName(), key)) {
throw new IllegalStateException("文件上传失败");
}
}
/**
*
*
* @param sourceKey KEY
* @param targetKey KEY
*/
public void move(String sourceKey, String targetKey) {
if (!client.doesObjectExist(configuration.getBucketName(), sourceKey)) {
throw new IllegalStateException("源文件信息不存在");
}
client.copyObject(configuration.getBucketName(), sourceKey, configuration.getBucketName(), targetKey);
if (!client.doesObjectExist(configuration.getBucketName(), targetKey)) {
throw new IllegalStateException("移动文件失败[1]");
}
if (!this.delete(sourceKey)) {
throw new IllegalStateException("移动文件失败[2]");
}
}
/**
* key访
*
* @param key key
* @param expireTime
*/
public URL generateUrl(String key, Long expireTime) throws Exception {
return generateUrl(key, expireTime, false);
}
/**
* key访
*
* @param key key
* @param expireTime
* @param down url
*/
public URL generateUrl(String key, Long expireTime, boolean down) throws Exception {
Assert.notNull(expireTime, "过期时间为空");
GeneratePresignedUrlRequest request;
Date expiration = new Date(System.currentTimeMillis() + expireTime);
request = new GeneratePresignedUrlRequest(this.configuration.getBucketName(), key);
request.setExpiration(expiration);
request.setMethod(HttpMethod.GET);
ResponseHeaderOverrides header = new ResponseHeaderOverrides();
// header.setContentType(this.deduceContentType(key));
if (down) {
//设置响应头强制下载
header.setContentDisposition("attachment;");
}
request.setResponseHeaders(header);
return this.client.generatePresignedUrl(request);
}
public String generateBase64(String key) {
if (key == null) {
return null;
}
try {
OSSObject ossObject = this.client.getObject(this.configuration.getBucketName(), key);
if (ossObject == null) {
return null;
}
InputStream inputStream = ossObject.getObjectContent();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
byte[] data = new byte[1024];
int length;
while ((length = inputStream.read(data)) != -1) {
outputStream.write(data, 0, length);
}
outputStream.close();
String base64 = Base64.getEncoder().encodeToString(outputStream.toByteArray());
return String.format("data:image/png;base64,%s", base64);
} catch (Exception e) {
log.error("generateBase64", e);
}
return null;
}
private String deduceContentType(String key) {
int index = key.lastIndexOf(".");
String fileExtension = null;
if (index != -1) {
fileExtension = key.substring(index);
}
//文件的后缀名
if (".bmp".equalsIgnoreCase(fileExtension)) {
return "image/bmp";
}
if (".gif".equalsIgnoreCase(fileExtension)) {
return "image/gif";
}
if (".jpeg".equalsIgnoreCase(fileExtension)) {
return "image/jpeg";
}
if (".jpg".equalsIgnoreCase(fileExtension)) {
return "image/jpg";
}
if (".png".equalsIgnoreCase(fileExtension)) {
return "image/png";
}
if (".html".equalsIgnoreCase(fileExtension)) {
return "text/html";
}
if (".txt".equalsIgnoreCase(fileExtension)) {
return "text/plain";
}
if (".vsd".equalsIgnoreCase(fileExtension)) {
return "application/vnd.visio";
}
if (".ppt".equalsIgnoreCase(fileExtension) || ".pptx".equalsIgnoreCase(fileExtension)) {
return "application/vnd.ms-powerpoint";
}
if (".doc".equalsIgnoreCase(fileExtension) || ".docx".equalsIgnoreCase(fileExtension)) {
return "application/msword";
}
if (".xml".equalsIgnoreCase(fileExtension)) {
return "text/xml";
}
if (".zip".equalsIgnoreCase(fileExtension)) {
return "application/zip";
}
if (".pdf".equalsIgnoreCase(fileExtension)) {
return "application/pdf";
}
if (".xls".equalsIgnoreCase(fileExtension) || ".xlsx".equalsIgnoreCase(fileExtension)) {
return "application/vnd.ms-excel";
}
//默认返回类型
return "image/jpeg";
}
public InputStream getObject(String key) throws Exception {
OSSObject ossObject = client.getObject(configuration.getBucketName(), key);
return ossObject.getObjectContent();
}
/**
*
*/
public void download(OSSClient client, String key, String filename)
throws OSSException, ClientException {
client.getObject(new GetObjectRequest(configuration.getBucketName(), key), new File(filename));
}
/**
* oss
*
* @return true false
*/
public boolean delete(String key) {
//检查是否是有效文件
boolean exists = client.doesObjectExist(configuration.getBucketName(), key);
if (exists) {
client.deleteObject(configuration.getBucketName(), key);
}
return exists;
}
public OSS getOriginalClient() {
return client;
}
private ClientBuilderConfiguration getClientBuilderConfiguration() {
ClientBuilderConfiguration config = new ClientBuilderConfiguration();
config.setProtocol(configuration.isHttps() ? Protocol.HTTPS : Protocol.HTTP);
return config;
}
private STSAssumeRoleSessionCredentialsProvider createSTSCredentialsProvider() {
try {
return CredentialsProviderFactory.newSTSAssumeRoleSessionCredentialsProvider(
configuration.getRegionId(),
configuration.getAccessKeyId(),
configuration.getAccessKeySecret(),
configuration.getRoleArn()
)
.withExpiredDuration(configuration.getExpiredDuration());
} catch (com.aliyuncs.exceptions.ClientException e) {
log.error("createStsCredentialsProvider ERROR", e);
throw new RuntimeException("create createStsCredentialsProvider error");
}
}
public OSSConfiguration getConfiguration() {
return this.configuration;
}
}

View File

@ -0,0 +1,33 @@
package com.ruoyi.framework.oss;
import lombok.Data;
/**
* @author liangyq
* @date 2025-03-24
*/
@Data
public class OSSConfiguration {
private String endpoint;
private String accessKeyId;
private String accessKeySecret;
private String bucketName;
private String regionId;
private String roleArn;
private boolean https;
private Long expiredDuration;
@Override
public OSSConfiguration clone() {
OSSConfiguration configuration = new OSSConfiguration();
configuration.setEndpoint(this.endpoint);
configuration.setAccessKeyId(this.accessKeyId);
configuration.setAccessKeySecret(this.getAccessKeySecret());
configuration.setBucketName(this.bucketName);
configuration.setRegionId(this.regionId);
configuration.setRoleArn(this.roleArn);
configuration.setHttps(this.https);
configuration.setExpiredDuration(this.expiredDuration);
return configuration;
}
}

View File

@ -17,12 +17,6 @@
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<!-- 通用工具-->
<dependency>
<groupId>com.ruoyi</groupId>