+ * 对象存储可以对图片进行处理 + * 图片处理 https://help.aliyun.com/document_detail/47505.html + * 图片样式 https://help.aliyun.com/document_detail/48884.html + *
+ * 对象存储鉴权:阿里云临时安全令牌(Security Token Service,STS) https://help.aliyun.com/document_detail/28756.htm?spm=a2c4g.11186623.2.4.3fba6d13JdXEzJ#reference-ong-5nv-xdb + * 对象存储oss docs 授权访问 使用STS进行临时授权 使用签名URL进行临时授权 https://help.aliyun.com/document_detail/32016.html?spm=a2c4g.11186623.6.992.7a943b4aPjkyTA#title-pu8-5o8-x7j + * 搜索 【sts】 + * + * @author yabo dazer + * @date 2019/8/6 19:02 + * //@see AliyunMsgUtil + */ +@Service +public class AliyunOssFileServiceImpl implements ISysFileService { + private static final Logger log = LoggerFactory.getLogger(AliyunOssFileServiceImpl.class); + @Autowired + private AliyunOssConfig aliyunOssConfig; + /** + * 这些阿里云访问id + */ + private String ACCESS_KEY_ID; + private String ACCESS_KEY_SECRET; + private String BUCKET_NAME; + /** + * https://oss.console.aliyun.com/ + * 这些阿里云 oss 参数都需要替换 + *+ * 如果是内网的话,访问速度肯定更快,内网不限制速度。 + */ + private String ENDPOINT = "oss-cn-shenzhen.aliyuncs.com"; + private String ENDPOINT_INTERNAL = ENDPOINT.replace(".aliyuncs.com", "-internal.aliyuncs.com"); + + /** + * 域名绑定 + * USER_DOMAIN_NAME: 域名名称, oss 访问路径绑定的用户自定义域名; 如果没有,就设置为null + * hostHttps: 是否开启了https, 需要在控制台配置 + * https://oss.console.aliyun.com/bucket/oss-cn-shanghai/hiber2019/domain + *
+ * private static final String USER_DOMAIN_NAME = "image.jl-media.cn"; + */ + private static final String USER_DOMAIN_NAME = null; + private static final boolean HOST_HTTPS = true; + + @PostConstruct + public void init() { + ACCESS_KEY_ID = aliyunOssConfig.getAccessKeyId(); + ACCESS_KEY_SECRET = aliyunOssConfig.getAccessKeySecret(); + BUCKET_NAME = aliyunOssConfig.getOssBucketName(); + ENDPOINT = aliyunOssConfig.getOssEndpoint(); + ENDPOINT_INTERNAL = ENDPOINT.replace(".aliyuncs.com", "-internal.aliyuncs.com"); + } + + /** + * demo 地址 https://help.aliyun.com/learn/learningpath/oss.html + *
+ * 简单demo https://github.com/aliyun/aliyun-oss-java-sdk/blob/master/src/samples/UploadSample.java
+ * 分片上传demo(大文件) https://github.com/aliyun/aliyun-oss-java-sdk/blob/master/src/samples/MultipartUploadSample.java 分片上传,在oss上面能看到碎片记录
+ *
+ * @return eg: https://hiber2019.oss-cn-shanghai.aliyuncs.com/upload/default/20190806202208849_jvs5g.png
+ */
+ @Override
+ public String uploadFile(MultipartFile file) throws Exception {
+ return this.uploadFile(file, null);
+ }
+
+ @Override
+ public String uploadFile(MultipartFile file, String modules) throws Exception {
+ //key: 这里不能以/开头
+ String newName = validateModule(file, null);
+ //key: 这里不能以/开头
+ String requestKey = "upload/" + "/" + newName;
+ //这里增加一个前缀区分一下是测试环境还是正式环境
+ boolean isProd = "prod".equalsIgnoreCase(SpringUtil.getActiveProfile());
+ if (!isProd) {
+ requestKey = SpringUtil.getActiveProfile() + "/" + requestKey;
+ }
+
+ long mb5 = 5 * 1024 * 1024L;
+ if (file.getSize() > mb5) {
+ //大于5mb,我们就分片上传
+ this.ossUploadFileBigMultiable(isProd ? ENDPOINT_INTERNAL : ENDPOINT, requestKey, file);
+ } else {
+ //否则,我们常规上传
+ this.ossUploadFileSmall(isProd ? ENDPOINT_INTERNAL : ENDPOINT, requestKey, file);
+ }
+
+ // 解析结果
+ // 注意,这里可能 需要 replace
+ String accessPath;
+ if (StringUtils.isNotBlank(USER_DOMAIN_NAME)) {
+ if (HOST_HTTPS) {
+ accessPath = "https://" + USER_DOMAIN_NAME + "/" + requestKey;
+ } else {
+ accessPath = "http://" + USER_DOMAIN_NAME + "/" + requestKey;
+ }
+ } else {
+ accessPath = "https://" + BUCKET_NAME + "." + ENDPOINT + "/" + requestKey;
+ }
+ return accessPath;
+ }
+
+ /**
+ * demo 地址 https://help.aliyun.com/learn/learningpath/oss.html
+ * https://github.com/aliyun/aliyun-oss-java-sdk/blob/master/src/samples/DeleteObjectsSample.java
+ */
+ @Override
+ public boolean deleteFile(String fileUrl) {
+ /*
+ * Constructs a client instance with your account for accessing OSS
+ */
+ OSS client = new OSSClientBuilder().build(ENDPOINT, ACCESS_KEY_ID, ACCESS_KEY_SECRET);
+ String storePath = getStorePath(fileUrl);
+ List
+ * 除了通过PUT Object接口上传文件到OSS以外,OSS还提供了另外一种上传模式——Multipart Upload。 https://help.aliyun.com/document_detail/31991.html
+ */
+ private String ossUploadFileBigMultiable(String endpoint, String picturePath, MultipartFile file) throws IOException {
+ String requestKey = String.valueOf(picturePath);
+ List
+ * private static ExecutorService executorService = Executors.newCachedThreadPool();
+ */
+ ExecutorService executorService = new ThreadPoolExecutor(50, 1000, 60L, TimeUnit.SECONDS,
+ new SynchronousQueue<>(), new ThreadFactoryBuilder().setNameFormat("aliyun-oss-upload(" + requestKey + ")-thread-pool-%d").build());
+
+ /*
+ * Constructs a client instance with your account for accessing OSS
+ */
+ ClientBuilderConfiguration conf = new ClientBuilderConfiguration();
+ conf.setIdleConnectionTime(1000);
+ OSS client = new OSSClientBuilder().build(endpoint, ACCESS_KEY_ID, ACCESS_KEY_SECRET, conf);
+
+ try {
+ /*
+ * Claim a upload id firstly
+ */
+ InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(BUCKET_NAME, requestKey);
+ InitiateMultipartUploadResult result = client.initiateMultipartUpload(request);
+ String uploadId = result.getUploadId();
+
+ /*
+ * Calculate how many parts to be divided
+ */
+ final long partSize = 5 * 1024 * 1024L; // 5MB
+ long fileLength = file.getSize();
+ int partCount = (int) (fileLength / partSize);
+ if (fileLength % partSize != 0) {
+ partCount++;
+ }
+ if (partCount > 10000) {
+ throw new RuntimeException("Total parts count should not exceed 10000");
+ }
+ /*
+ * Upload multiparts to your bucket
+ */
+ if (log.isInfoEnabled()) {
+ log.info("Begin to upload multiparts to OSS from a file\n");
+ }
+ for (int i = 0; i < partCount; i++) {
+ long startPos = i * partSize;
+ long curPartSize = (i + 1 == partCount) ? (fileLength - startPos) : partSize;
+ executorService.execute(new PartUploader(client, requestKey, partETags, file, startPos, curPartSize, i + 1, uploadId));
+ }
+
+ /*
+ * Waiting for all parts finished
+ */
+ executorService.shutdown();
+ while (!executorService.isTerminated()) {
+ try {
+ executorService.awaitTermination(5, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ /* try {
+ //等待所有任务执行完毕, 这里 不限定20分钟必须执行完毕
+ countDownLatch.await(60 * 20, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ } finally {
+ log.error("所有线程都已经执行完毕...., 一共开启线程数量: {}", partCount);
+ }*/
+
+ /*
+ * Verify whether all parts are finished
+ */
+ if (partETags.size() != partCount) {
+ throw new RuntimeException("Upload multiparts fail due to some parts are not finished yet");
+ } else {
+ if (log.isInfoEnabled()) {
+ log.info("Succeed to complete multiparts into an object named {} \n", requestKey);
+ }
+ }
+
+ /*
+ * View all parts uploaded recently
+ */
+ listAllParts(client, requestKey, uploadId);
+
+ /*
+ * Complete to upload multiparts
+ */
+ completeMultipartUpload(client, partETags, requestKey, uploadId);
+
+ /*
+ * Fetch the object that newly created at the step below.
+ */
+ if (log.isInfoEnabled()) {
+ log.info("Fetching an object");
+ }
+ OSSObject ossObject = client.getObject(new GetObjectRequest(BUCKET_NAME, requestKey));
+ if (log.isInfoEnabled()) {
+ log.info(ossObject.getKey());
+ }
+ } catch (OSSException oe) {
+ log.error("Caught an OSSException, which means your request made it to OSS, "
+ + "but was rejected with an error response for some reason.");
+ log.error("Error Message: {}", oe.getErrorMessage());
+ log.error("Error Code {}: ", oe.getErrorCode());
+ log.error("Request ID {}: ", oe.getRequestId());
+ log.error("Host ID {}: ", oe.getHostId());
+ log.error("");
+
+ log.error("图片上传失败(OSS)", oe);
+ oe.printStackTrace();
+ throw new IOException("图片上传失败(OSS Multipart)");
+ } catch (ClientException ce) {
+ log.error("Caught an ClientException, which means the client encountered "
+ + "a serious internal problem while trying to communicate with OSS, "
+ + "such as not being able to access the network.");
+ log.error("Error Message: " + ce.getMessage());
+ log.error("");
+
+ log.error("图片上传失败(Client)", ce);
+ ce.printStackTrace();
+ throw new IOException("图片上传失败(Client Multipart)");
+ } finally {
+ /*
+ * Do not forget to shut down the client finally to release all allocated resources.
+ */
+ if (client != null) {
+ client.shutdown();
+ }
+ }
+
+ return picturePath;
+ }
+
+ private class PartUploader implements Runnable {
+ private MultipartFile file;
+ private long startPos;
+
+ private long partSize;
+ private int partNumber;
+ private String uploadId;
+
+ private OSS client;
+ private String requestKey;
+ private final List
+ * private static final String USER_DOMAIN_NAME = "image.jl-media.cn";
+ */
+ private static final String USER_DOMAIN_NAME = null;
+ private static final boolean HOST_HTTPS = true;
+
+ /**
+ * ceph配置初始化
+ */
+ static {
+ log.info("开始初始化ceph配置");
+ AWSCredentials credentials = new BasicAWSCredentials(ACCESS_KEY, SECRET_KEY);
+ ClientConfiguration clientConfiguration = new ClientConfiguration();
+ clientConfiguration.setProtocol(Protocol.HTTP);
+ amazonS3 = new AmazonS3Client(credentials, clientConfiguration);
+ amazonS3.setEndpoint(HOST);
+ log.info("ceph配置初始化成功");
+ }
+
+ /**
+ * 使用s3 api 创建 ceph bucket
+ *
+ * @param bucketname
+ * @return
+ */
+ public Bucket createBucket(String bucketname) {
+ Bucket bucket = amazonS3.createBucket(bucketname);
+ log.info("bucket name is {}", bucket);
+ return bucket;
+ }
+
+ /**
+ * 根据 bucketName 和 filename 获取指定文件返回输入流
+ */
+ public S3ObjectInputStream getObject(String bucketName, String filename) {
+ S3Object s3Object = amazonS3.getObject(new GetObjectRequest(bucketName, filename));
+ S3ObjectInputStream s3ObjectInputStream = s3Object.getObjectContent();
+ return s3ObjectInputStream;
+ }
+
+ @Override
+ public String uploadFile(MultipartFile file) throws Exception {
+ return this.uploadFile(file, null);
+ }
+
+ @Override
+ public String uploadFile(MultipartFile file, String modules) throws Exception {
+ //key: 这里不能以/开头
+ String newName = validateModule(file, null);
+ //key: 这里不能以/开头
+ String requestKey = "upload/" + newName;
+ //这里增加一个前缀区分一下是测试环境还是正式环境
+ boolean isProd = "prod".equalsIgnoreCase(SpringUtil.getActiveProfile());
+ if (!isProd) {
+ requestKey = SpringUtil.getActiveProfile() + "/" + requestKey;
+ }
+
+ // long mb5 = 5 * 1024 * 1024L;
+ //大于5mb,我们就分片上传
+ PutObjectResult result = amazonS3.putObject(BUCKET_NAME, requestKey, file.getInputStream(), new ObjectMetadata());
+ // 上传成功
+ if (result.isRequesterCharged()) {
+ // 解析结果
+ // 注意,这里可能 需要 replace
+ String accessPath;
+ if (StringUtils.isNotBlank(USER_DOMAIN_NAME)) {
+ if (HOST_HTTPS) {
+ accessPath = "https://" + USER_DOMAIN_NAME + "/" + requestKey;
+ } else {
+ accessPath = "http://" + USER_DOMAIN_NAME + "/" + requestKey;
+ }
+ } else {
+ accessPath = "https://" + BUCKET_NAME + "/" + requestKey;
+ }
+ return accessPath;
+ }
+ return null;
+ }
+
+ @Override
+ public boolean deleteFile(String fileUrl) {
+ if (StringUtils.isEmpty(fileUrl)) {
+ return false;
+ }
+ String storePath = getStorePath(fileUrl);
+ amazonS3.deleteObject(BUCKET_NAME, storePath);
+ return true;
+ }
+
+ @Override
+ public String listObject() {
+ return null;
+ }
+
+ /**
+ * 转换url
+ *
+ * @param filePath https://hiber2019.oss-cn-shanghai.aliyuncs.com/upload/default/20190806202208849_jvs5g.png
+ * @return upload/default/20190806202208849_jvs5g.png
+ */
+ private String getStorePath(String filePath) {
+ String publicPath1 = "https://" + BUCKET_NAME + "/";
+ String publicPath2 = "http://" + BUCKET_NAME + "/";
+ String publicPath3 = "https://" + USER_DOMAIN_NAME + "/";
+ String publicPath4 = "http://" + USER_DOMAIN_NAME + "/";
+ //String publicPath5 = ServletCacheUtils.getInstance().getHttpRootPath();
+
+ filePath = filePath.replace(publicPath1, "");
+ filePath = filePath.replace(publicPath2, "");
+ filePath = filePath.replace(publicPath3, "");
+ filePath = filePath.replace(publicPath4, "");
+ //filePath = filePath.replace(publicPath5, "");
+ return filePath;
+ }
+}
diff --git a/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/service/FastDfsSysFileServiceImpl.java b/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/service/FastDfsSysFileServiceImpl.java
index deee22ee..76dbbe86 100644
--- a/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/service/FastDfsSysFileServiceImpl.java
+++ b/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/service/FastDfsSysFileServiceImpl.java
@@ -1,6 +1,11 @@
package com.ruoyi.file.service;
+import com.github.tobato.fastdfs.domain.conn.PooledConnectionFactory;
+import com.github.tobato.fastdfs.exception.FdfsUnsupportStorePathException;
import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
@@ -10,14 +15,15 @@ import com.github.tobato.fastdfs.service.FastFileStorageClient;
/**
* FastDFS 文件存储
- *
* @author ruoyi
*/
@Service
public class FastDfsSysFileServiceImpl implements ISysFileService
{
+ private final Logger logger = LoggerFactory.getLogger(FastDfsSysFileServiceImpl.class);
/**
* 域名或本机访问地址
+ * FastDFS配置 其他参数见:{@link PooledConnectionFactory}
*/
@Value("${fdfs.domain}")
public String domain;
@@ -35,8 +41,38 @@ public class FastDfsSysFileServiceImpl implements ISysFileService
@Override
public String uploadFile(MultipartFile file) throws Exception
{
+ return this.uploadFile(file);
+ }
+
+ @Override
+ public String uploadFile(MultipartFile file, String modules) throws Exception {
+ // fastdsf 这里的 modules 没用
+ validateModule(file, modules);
+
StorePath storePath = storageClient.uploadFile(file.getInputStream(), file.getSize(),
FilenameUtils.getExtension(file.getOriginalFilename()), null);
+
+ /// fileUrl = "http://127.0.0.1:22122/" + storePath.getFullPath();
return domain + "/" + storePath.getFullPath();
}
+
+ @Override
+ public boolean deleteFile(String fileUrl) {
+ if (StringUtils.isEmpty(fileUrl)) {
+ return false;
+ }
+ try {
+ StorePath storePath = StorePath.parseFromUrl(fileUrl);
+ storageClient.deleteFile(storePath.getGroup(), storePath.getPath());
+ return true;
+ } catch (FdfsUnsupportStorePathException e) {
+ logger.warn(e.getMessage());
+ }
+ return false;
+ }
+
+ @Override
+ public String listObject() {
+ return null;
+ }
}
diff --git a/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/service/FtpFileServiceImpl.java b/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/service/FtpFileServiceImpl.java
new file mode 100644
index 00000000..67123a69
--- /dev/null
+++ b/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/service/FtpFileServiceImpl.java
@@ -0,0 +1,79 @@
+package com.ruoyi.file.service;
+
+import cn.hutool.extra.ftp.Ftp;
+import cn.hutool.extra.ftp.FtpMode;
+import com.ruoyi.file.config.FtpConfig;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.IOException;
+
+/**
+ * ftp目录用来上传文件;
+ * ftp, 如:iis、linux ftp、vsftpd、FileZilla Server,需要自己搭建服务
+ * @author dazer
+ */
+@Service
+public class FtpFileServiceImpl implements ISysFileService {
+ @Autowired
+ private FtpConfig ftpConfig;
+ public static final String ACCESS_PREFIX = "";
+
+ @Override
+ public String uploadFile(MultipartFile file) throws Exception {
+ return this.uploadFile(file, null);
+ }
+
+ @Override
+ public String uploadFile(MultipartFile file, String modules) throws Exception {
+ String fileName = "upload/" + validateModule(file, modules);
+
+ Ftp ftp = null;
+ try {
+ ftp = new Ftp(ftpConfig.getHostName(), ftpConfig.getPort(), ftpConfig.getUserName(), ftpConfig.getPassword());
+ ftp.cd("");
+ ftp.setMode(FtpMode.Passive);
+ ftp.upload("", fileName, file.getInputStream());
+ } finally {
+ if (ftp != null) {
+ ftp.close();
+ }
+ }
+ return ftpConfig.getHostName() + "/" + fileName;
+ }
+
+ @Override
+ public boolean deleteFile(String fileUrl) {
+ Ftp ftp = null;
+ try {
+ ftp = new Ftp(ftpConfig.getHostName(), ftpConfig.getPort(), ftpConfig.getUserName(), ftpConfig.getPassword());
+ String storePath = getStorePath(fileUrl);
+ return ftp.delFile(storePath);
+ } finally {
+ if (ftp != null) {
+ try {
+ ftp.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ @Override
+ public String listObject() {
+ return null;
+ }
+
+ private String getStorePath(String filePath) {
+ int groupStartPos = -1;
+ if ((groupStartPos = filePath.indexOf(ACCESS_PREFIX) + ACCESS_PREFIX.length()) + 1 == 0) {
+ groupStartPos = 0;
+ //throw new RrException("解析文件路径错误,被解析路径url没有" + Constant.SERVIER_NAME_SUFFIX + ",当前解析路径为".concat(filePath));
+ }
+ // 获取group起始位置
+ String groupAndPath = filePath.substring(groupStartPos);
+ return groupAndPath + "";
+ }
+}
diff --git a/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/service/ISysFileService.java b/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/service/ISysFileService.java
index 5a353489..26330a6e 100644
--- a/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/service/ISysFileService.java
+++ b/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/service/ISysFileService.java
@@ -1,14 +1,44 @@
package com.ruoyi.file.service;
+import com.ruoyi.common.core.exception.file.FileNameLengthLimitExceededException;
+import com.ruoyi.common.core.exception.file.InvalidExtensionException;
+import com.ruoyi.common.core.utils.file.MimeTypeUtils;
+import com.ruoyi.file.utils.FileUploadUtils;
+import org.apache.commons.lang3.RandomStringUtils;
+import org.apache.commons.lang3.StringUtils;
import org.springframework.web.multipart.MultipartFile;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import static com.ruoyi.file.utils.FileUploadUtils.assertAllowed;
+import static com.ruoyi.file.utils.FileUploadUtils.extractFilename;
+
/**
* 文件上传接口
- *
+ * 1: default: 最原始的java文件上传
+ * 2: ftp 使用ftp模拟文件服务器; 如:iis、linux ftp、vsftpd、FileZilla Server,需要自己搭建服务
+ * 3: FastDfs 是淘宝开源的分布式文件系统; 淘宝 开发的分布式 dfs, 需要自己搭建服务 (FastDFS)
+ * 4: minio 轻量级分布式文件系统; 类似一个阿里云oss、腾讯COS的一个开源、轻量级别的对象存储付;
+ * 5: aliyun oss; aliyun oss https://help.aliyun.com/learn/learningpath/oss.html ,需要购买
+ * 6: CEPH 分布式大数据文件存储系统 http://docs.ceph.org.cn/
* @author ruoyi
*/
public interface ISysFileService
{
+ /**
+ * 允许上传文件存放的目录
+ * 不同项目,这里可能做不同的修改;不过不想区分,就default;
+ * 项目稍微大一些,如果不区分目录,后期要做删除 or 迁移就很麻烦;
+ */
+ String[] DEFAULT_MODULES_NAME = {
+ // 图片
+ "default", "banner", "product", "images", "music",
+ // pdf
+ "pdf" };
+
/**
* 文件上传接口
*
@@ -16,5 +46,52 @@ public interface ISysFileService
* @return 访问地址
* @throws Exception
*/
- public String uploadFile(MultipartFile file) throws Exception;
+ String uploadFile(MultipartFile file) throws Exception;
+
+ String uploadFile(MultipartFile file, String modules) throws Exception;
+
+ /**
+ * 删除文件
+ *
+ * @param fileUrl 文件访问地址,全路径或者不是全路径都可以
+ * @return
+ */
+ boolean deleteFile(String fileUrl);
+
+ /**
+ * 获取文件占用空间
+ * 别名:objectsCapacity
+ * @return 文件大小字符串,eg: 100MB、2G
+ */
+ String listObject();
+
+ /**
+ * 校验文件名称长度 & 校验文件大小 & 校验上传的目录是否是项目中注册了的 & 返回新的文件名称
+ *
+ * @param file 文件
+ * @param modules 模块,这里作为上传的文件夹使用;eg: 项目中有banner、video、music、txt、product、default 多个模块,不同模块存放到不同文件夹中;
+ * @return 新的系统生成的文件名称
+ * @throws InvalidExtensionException
+ */
+ default String validateModule(MultipartFile file, String modules) throws InvalidExtensionException {
+ Objects.requireNonNull(file, "文件不能为空!");
+ modules = StringUtils.defaultString(modules, "default");
+
+ //1、这里校验上传文件的模块,如果没有注册,直接报错
+ if (!Arrays.stream(DEFAULT_MODULES_NAME).collect(Collectors.toList()).contains(modules)) {
+ throw new RuntimeException("上传模块" + modules + "不存在,请现在 'FolderPath.UploadModules'中注册. 枚举值,请参见Home接口:api/getFolderPath");
+ }
+
+ // 2、校验文件名称长度
+ /// String ext = (String.valueOf(file.getOriginalFilename()).substring(String.valueOf(file.getOriginalFilename()).lastIndexOf("."))).toLowerCase();
+ int fileNamelength = file.getOriginalFilename().length();
+ if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH)
+ {
+ throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH);
+ }
+
+ // 3、文件大小校验
+ assertAllowed(file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
+ return modules + "/" + extractFilename(file);
+ }
}
diff --git a/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/service/LocalSysFileServiceImpl.java b/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/service/LocalSysFileServiceImpl.java
index c0e20681..4414b14a 100644
--- a/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/service/LocalSysFileServiceImpl.java
+++ b/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/service/LocalSysFileServiceImpl.java
@@ -1,5 +1,6 @@
package com.ruoyi.file.service;
+import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;
@@ -17,18 +18,21 @@ public class LocalSysFileServiceImpl implements ISysFileService
{
/**
* 资源映射路径 前缀
+ * eg: eg: /statics
*/
@Value("${file.prefix}")
public String localFilePrefix;
/**
* 域名或本机访问地址
+ * eg: http://127.0.0.1:9300
*/
@Value("${file.domain}")
public String domain;
/**
* 上传文件存储在本地的根路径
+ * eg: D:/ruoyi/uploadPath
*/
@Value("${file.path}")
private String localFilePath;
@@ -43,8 +47,23 @@ public class LocalSysFileServiceImpl implements ISysFileService
@Override
public String uploadFile(MultipartFile file) throws Exception
{
- String name = FileUploadUtils.upload(localFilePath, file);
+ return this.uploadFile(file, null);
+ }
+
+ @Override
+ public String uploadFile(MultipartFile file, String modules) throws Exception {
+ String name = FileUploadUtils.upload(localFilePath + "/" + StringUtils.defaultString(modules, ""), file);
String url = domain + localFilePrefix + name;
return url;
}
+
+ @Override
+ public boolean deleteFile(String fileUrl) {
+ return false;
+ }
+
+ @Override
+ public String listObject() {
+ return null;
+ }
}
diff --git a/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/service/MinioSysFileServiceImpl.java b/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/service/MinioSysFileServiceImpl.java
index 3dd2fc6f..e1bd8c62 100644
--- a/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/service/MinioSysFileServiceImpl.java
+++ b/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/service/MinioSysFileServiceImpl.java
@@ -1,5 +1,8 @@
package com.ruoyi.file.service;
+import cn.hutool.extra.spring.SpringUtil;
+import io.minio.RemoveObjectArgs;
+import io.minio.errors.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
@@ -8,6 +11,10 @@ import com.ruoyi.file.utils.FileUploadUtils;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
+import java.io.IOException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+
/**
* Minio 文件存储
*
@@ -32,7 +39,17 @@ public class MinioSysFileServiceImpl implements ISysFileService
@Override
public String uploadFile(MultipartFile file) throws Exception
{
- String fileName = FileUploadUtils.extractFilename(file);
+ return this.uploadFile(file, null);
+ }
+
+ @Override
+ public String uploadFile(MultipartFile file, String modules) throws Exception {
+ String fileName = validateModule(file ,modules);
+ boolean isProd = "prod".equalsIgnoreCase(SpringUtil.getActiveProfile());
+ if (!isProd) {
+ fileName = SpringUtil.getActiveProfile() + "/" + fileName;
+ }
+
PutObjectArgs args = PutObjectArgs.builder()
.bucket(minioConfig.getBucketName())
.object(fileName)
@@ -42,4 +59,32 @@ public class MinioSysFileServiceImpl implements ISysFileService
client.putObject(args);
return minioConfig.getUrl() + "/" + minioConfig.getBucketName() + "/" + fileName;
}
+
+ @Override
+ public boolean deleteFile(String fileUrl) {
+ RemoveObjectArgs args = RemoveObjectArgs.builder().
+ bucket(minioConfig.getBucketName()).
+ object(fileUrl).
+ build();
+ try {
+ client.removeObject(args);
+ return true;
+ } catch (ErrorResponseException |
+ InsufficientDataException |
+ InternalException |
+ InvalidKeyException |
+ InvalidResponseException |
+ IOException |
+ NoSuchAlgorithmException |
+ ServerException |
+ XmlParserException e) {
+ e.printStackTrace();
+ }
+ return false;
+ }
+
+ @Override
+ public String listObject() {
+ return null;
+ }
}