编写上传下载组件,优势:
①通过spring.factories注册,便于被多个微服务共同依赖
②oss上传使用s3 sdk,可接入其他oss服务商
③可以接入新的上传模式,通过mode一键切换
上传工具类
放在common或其他方便被所有微服务共同依赖的模块
位置:ruoyi-common/src/main/java/com/summer/upload
统一接口
统一对外接口,负责整合所有上传方式、根据mode自动切换上传方式
java
package com.summer.upload;
import com.ruoyi.common.exception.ServiceException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.io.InputStream;
/**
* 文件上传下载组件(集成各上传工具类)
*
* @author summer
*/
@Service
public class UploadComponent {
@Value("${upload.mode}")
private Integer mode;
@Autowired(required = false)
private UploadUtil uploadUtil;
@Autowired(required = false)
private OssUtil ossUtil;
/**
* 基础上传接口
*
* @param filePath 示例:cat.jpg、pet/cat.jpg
*/
public String upload(String filePath, InputStream inputStream) {
try (InputStream fileStream = inputStream) {
// 1.本地上传
if (mode == 1) {
return uploadUtil.upload(filePath, fileStream);
}
// 2.oss上传
if (mode == 2) {
return ossUtil.upload(filePath, fileStream);
}
return "";
} catch (Exception e) {
throw new ServiceException("上传失败:" + e.getMessage());
}
}
/**
* 基础下载接口
*
* @param filePath 示例:cat.jpg、pet/cat.jpg
*/
public InputStream download(String filePath) {
try {
// 1.本地文件下载
if (mode == 1) {
return uploadUtil.download(filePath);
}
// 2.oss文件下载
if (mode == 2) {
return ossUtil.download(filePath);
}
return null;
} catch (Exception e) {
throw new ServiceException("下载失败:" + e.getMessage());
}
}
}
本地上传
java
package com.summer.upload;
import cn.hutool.core.io.FileUtil;
import com.ruoyi.common.exception.ServiceException;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Value;
import java.io.File;
import java.io.InputStream;
/**
* 本地上传工具类
*
* @author summer
*/
@Setter
public class UploadUtil {
@Value("${upload.local.dir}")
private String dir;
@Value("${upload.local.link}")
private Boolean link;
@Value("${upload.local.domain}")
private String domain;
@Value("${upload.local.bucket}")
private String bucket;
/**
* 上传文件
*
* @param filePath 示例:cat.jpg、pet/cat.jpg
*/
public String upload(String filePath, InputStream inputStream) {
// 确保目录存在并保存
String localFilePath = FileUtil.file(dir, filePath).getAbsolutePath();
FileUtil.mkParentDirs(localFilePath);
FileUtil.writeFromStream(inputStream, localFilePath);
// 文件返回模式(http链接模式需配置文件服务)
String url = domain + "/" + bucket + "/" + filePath;
return link ? url : filePath;
}
/**
* 下载文件
*
* @param filePath 示例:cat.jpg、pet/cat.jpg
*/
public InputStream download(String filePath) {
// 获取本地文件
File file = new File(dir + "/" + filePath);
if (!file.exists()) {
throw new ServiceException("文件不存在:" + filePath);
}
return FileUtil.getInputStream(file);
}
}
oss上传
添加依赖
xml
<!-- oss客户端 -->
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-s3</artifactId>
<version>1.12.676</version>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-sts</artifactId>
<version>1.12.676</version>
</dependency>
代码
java
package com.summer.upload;
import cn.hutool.core.io.FileUtil;
import com.amazonaws.ClientConfiguration;
import com.amazonaws.Protocol;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.ruoyi.common.exception.ServiceException;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Value;
import javax.annotation.PostConstruct;
import java.io.InputStream;
/**
* oss上传工具类
*
* @author summer
*/
@Setter
public class OssUtil {
@Value("${upload.oss.accessKeyId}")
private String accessKeyId;
@Value("${upload.oss.accessKeySecret}")
private String accessKeySecret;
@Value("${upload.oss.endPoint}")
private String endPoint;
@Value("${upload.oss.domain}")
private String domain;
@Value("${upload.oss.bucket}")
private String bucket;
@Value("${upload.oss.link}")
private Boolean link;
private AmazonS3 oss;
@PostConstruct
private void init() {
AWSCredentials credentials = new BasicAWSCredentials(accessKeyId, accessKeySecret);
ClientConfiguration clientConfig = new ClientConfiguration();
clientConfig.setProtocol(Protocol.HTTP);
oss = AmazonS3ClientBuilder.standard()
.withCredentials(new AWSStaticCredentialsProvider(credentials))
.withClientConfiguration(clientConfig)
.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(endPoint, Regions.DEFAULT_REGION.toString()))
.enablePathStyleAccess()
.build();
}
/**
* 上传文件
*
* @param filePath 示例:cat.jpg、pet/cat.jpg
*/
public String upload(String filePath, InputStream inputStream) {
try {
// 识别文件类型
ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentType(FileUtil.getMimeType(filePath));
// 上传文件
oss.putObject(bucket, filePath, inputStream, metadata);
String url = domain + "/" + bucket + "/" + filePath;
return link ? url : filePath;
} catch (Exception e) {
throw new ServiceException("OSS上传失败:" + e.getMessage());
}
}
/**
* 下载文件
*
* @param filePath 示例:cat.jpg、pet/cat.jpg
*/
public InputStream download(String filePath) {
try {
return oss.getObject(bucket, filePath).getObjectContent();
} catch (Exception e) {
throw new ServiceException("OSS下载失败:" + e.getMessage());
}
}
}
自动配置类
用于将上传工具类注册到Bean容器,在服务器注入配置以及使用
上传工具不在某个具体微服务的启动类扫描范围内,所以需要进行一些配置才能注入到Bean容器
Configuration
位置:ruoyi-common/src/main/java/com/summer/upload/
java
package com.summer.upload;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 文件上传工具自动配置
*
* @author summer
*/
@Configuration
public class UploadAutoConfiguration {
/**
* 统一上传组件
*/
@Bean
public UploadComponent uploadComponent() {
return new UploadComponent();
}
/**
* 本地上传工具配置
*/
@Bean
@ConditionalOnProperty(name = "upload.mode", havingValue = "1")
public UploadUtil localUploadUtil() {
return new UploadUtil();
}
/**
* OSS上传工具配置
*/
@Bean
@ConditionalOnProperty(name = "upload.mode", havingValue = "2")
public OssUtil ossUtil() {
return new OssUtil();
}
}
spring.factories
进入common模块资源目录:ruoyi-common/src/main/resources/
springboot2,使用 spring.factories:
资源目录下创建文件:META-INF/spring.factories
ini
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.summer.upload.UploadAutoConfiguration
springboot3,使用新机制:
资源目录下创建文件:META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
com.summer.upload.UploadAutoConfiguration
微服务调用
yml
假设admin服务需要调用
在application.yml中添加配置
yml
# 上传配置
upload:
# 1-本地上传,2-oss上传
mode: 2
local:
dir: upload
link: true
domain: https://xxx
bucket: test
oss:
accessKeyId: xxx
accessKeySecret: xxx
endPoint: http://xxx
domain: https://xxx
bucket: test
link: true
service
service调用上传组件,并扩充更多重载方法、入库逻辑、文件名逻辑等自定义逻辑
java
package com.ruoyi.web.service;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import com.ruoyi.common.exception.ServiceException;
import com.summer.upload.UploadComponent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.InputStream;
/**
* @author summer
*/
@Service
public class FileService {
@Autowired
private UploadComponent uploadComponent;
/**
* 拓展上传方法 - http文件入参
*/
public String upload(MultipartFile file) {
try {
return this.upload(file.getOriginalFilename(), file.getInputStream());
} catch (Exception e) {
throw new ServiceException("上传失败:" + e.getMessage());
}
}
/**
* 上传文件名替换规则
*
* @param filePath 示例:cat.jpg、pet/cat.jpg
*/
private String buildFileName(String filePath) {
// 去除开头多余斜杠
filePath = filePath.replaceAll("^/+", "");
// 将文件名替换为UUID避免特殊字符
String oldName = FileUtil.getName(filePath);
String newName = oldName.replaceAll(FileUtil.mainName(oldName), IdUtil.simpleUUID());
return filePath.replaceAll(oldName, newName);
}
/**
* 基础上传接口
*
* @param filePath 示例:cat.jpg、pet/cat.jpg
*/
public String upload(String filePath, InputStream inputStream) {
// 重构文件名
filePath = buildFileName(filePath);
return uploadComponent.upload(filePath, inputStream);
}
/**
* 基础下载接口
*
* @param filePath 示例:cat.jpg、pet/cat.jpg
*/
public InputStream download(String filePath) {
return uploadComponent.download(filePath);
}
}
controller
java
package com.ruoyi.web.controller.common;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import com.ruoyi.common.core.domain.common.R;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.web.service.FileService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
/**
* @author summer
*/
@RestController
@RequestMapping("/file")
@Api(tags = "文件上传")
public class FileController {
@Autowired
private FileService fileService;
/**
* 上传通用文件 - 单个
*/
@ApiOperation("上传通用文件")
@PostMapping("/upload")
public R<String> upload(MultipartFile file) {
return R.OK(fileService.upload(file));
}
/**
* 查看或下载文件
*/
@ApiOperation("查看或下载文件")
@GetMapping("/view/**")
public void viewFile(@RequestParam(defaultValue = "false") Boolean download, HttpServletRequest request, HttpServletResponse response) throws IOException {
// 相对路径获取文件
String filePath = request.getRequestURI().substring("/file/view/".length());
InputStream inputStream = fileService.download(filePath);
// 补充文件类型
String fileName = FileUtil.getName(filePath);
String mimeType = FileUtil.getMimeType(fileName);
response.setContentType(mimeType);
// 下载模式
if (download) {
response.setHeader("Content-Disposition", "attachment;fileName=" + fileName);
}
IoUtil.copy(inputStream, response.getOutputStream());
}
}