+ * 上传地址和访问地址经常是不一样的 + * 1、上传地址是内网;访问地址是外网; + * 2、上传地址是ip; 访问地址域名; + * 3、上传地址是http; 访问地址是https + * 4: eg: https://image.jl-media.cn + */ + private String domain = null; + /** + * 过期时间,单位秒; + * 如:1小时就写:3600L + * 如:9小时就写:32400L + * 如:12小时就写:43200L, 【不支持】,最大是 32400(9小时), 最小 1(1秒钟) + * 如:-1: 就永不过期,原样返回url + * 签名URL的默认过期时间为3600秒,最大值为32400秒 + * 文档:对象存储 授权访问 https://cloud.tencent.com/document/product/436/10199 + *
+ * 注意!!:tencent cos 设置Bucket ACL
+ * 1、私有 【必须要加签之后才能访问】
+ * 2、公共读
+ * 3、公共读写
+ */
+ private Long expiryDuration = 32400L;
+
+ public String getAccessKey() {
+ return accessKey;
+ }
+
+ public void setAccessKey(String accessKey) {
+ this.accessKey = accessKey;
+ }
+
+ public String getSecretKey() {
+ return secretKey;
+ }
+
+ public void setSecretKey(String secretKey) {
+ this.secretKey = secretKey;
+ }
+
+ public String getBucketName() {
+ return bucketName;
+ }
+
+ public void setBucketName(String bucketName) {
+ this.bucketName = bucketName;
+ }
+
+ public String getEndpoint() {
+ return endpoint;
+ }
+
+ public void setEndpoint(String endpoint) {
+ this.endpoint = endpoint;
+ }
+
+ public String getDomain() {
+ return domain;
+ }
+
+ public void setDomain(String domain) {
+ this.domain = domain;
+ }
+
+ public Long getExpiryDuration() {
+ return expiryDuration;
+ }
+
+ public void setExpiryDuration(Long expiryDuration) {
+ this.expiryDuration = expiryDuration;
+ }
+}
diff --git a/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/service/FastDfsServiceImpl.java b/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/service/FastDfsServiceImpl.java
index 23c00f54..cd093e7f 100644
--- a/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/service/FastDfsServiceImpl.java
+++ b/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/service/FastDfsServiceImpl.java
@@ -21,7 +21,7 @@ import com.github.tobato.fastdfs.service.FastFileStorageClient;
* @author ruoyi
* @see FastDfsConfig
*/
-@Primary
+//@Primary
@Service()
public class FastDfsServiceImpl implements IDfsService
{
diff --git a/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/service/TencentCosServiceImpl.java b/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/service/TencentCosServiceImpl.java
new file mode 100644
index 00000000..3c761166
--- /dev/null
+++ b/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/service/TencentCosServiceImpl.java
@@ -0,0 +1,198 @@
+package com.ruoyi.file.service;
+
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.extra.spring.SpringUtil;
+import com.qcloud.cos.COSClient;
+import com.qcloud.cos.exception.CosClientException;
+import com.qcloud.cos.http.HttpMethodName;
+import com.qcloud.cos.model.*;
+import com.ruoyi.common.core.exception.CustomException;
+import com.ruoyi.common.core.utils.StringUtils;
+import com.ruoyi.file.config.TencentCosConfig;
+import org.springframework.context.annotation.Primary;
+import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.net.URL;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 腾讯云cos 文件服务器实现
+ *
+ * @author yabo
+ * @see TencentCosConfig
+ */
+@Primary
+@Service()
+public class TencentCosServiceImpl implements IDfsService {
+ private final COSClient cosClient;
+ private final TencentCosConfig config;
+
+ public TencentCosServiceImpl(COSClient cosClient, TencentCosConfig config) {
+ this.cosClient = cosClient;
+ this.config = config;
+ }
+
+ @Override
+ public String uploadFile(MultipartFile file) throws Exception {
+ return this.uploadFile(file, null);
+ }
+
+ /**
+ * 腾讯云文档中心==》对象存储==》JAVA SDK===> 上传对象
+ * https://cloud.tencent.com/document/product/436/10199#.E4.B8.8A.E4.BC.A0.E5.AF.B9.E8.B1.A1
+ * 上传成功示例:https://cos.ityun.ltd/dev/upload/default/20210729-2be9b217-22ef-45a2-bfda-e793ffe33f55.jpeg
+ */
+ @Override
+ public String uploadFile(MultipartFile file, String modules) throws Exception {
+ //key: 这里不能以/开头
+ validateModule(file, null);
+ String newName = extractFileNameSimple(file);
+ //key: 这里不能以/开头
+ String requestKey = "upload/" + StringUtils.defaultString(modules, "default") + "/" + newName;
+ //这里增加一个前缀区分一下是测试环境还是正式环境
+ boolean isProd = "prod".equalsIgnoreCase(SpringUtil.getActiveProfile());
+ if (!isProd) {
+ requestKey = SpringUtil.getActiveProfile() + "/" + requestKey;
+ }
+
+ if (StringUtils.isBlank(config.getBucketName())) {
+ throw new CustomException("tencent cos bucket name 不能为空,请检查!");
+ }
+ if (StringUtils.isBlank(config.getEndpoint())) {
+ throw new CustomException("tencent cos endpoint(Region) 不能为空,请检查!");
+ }
+ ObjectMetadata objectMetadata = new ObjectMetadata();
+
+ // 指定文件将要存放的存储桶
+ String bucketName = config.getBucketName();
+ // 指定文件上传到 COS 上的路径,即对象键。例如对象键为folder/picture.jpg,则表示将文件 picture.jpg 上传到 folder 路径下
+ String key = requestKey;
+ PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, key, file.getInputStream(), objectMetadata);
+ try {
+ PutObjectResult putObjectResult = cosClient.putObject(putObjectRequest);
+ return (config.getDomain() + "/" + key);
+ } catch (CosClientException e) {
+ e.printStackTrace();
+ throw new CustomException("tencent cos upload 失败;错误代码:" + e.getErrorCode() + ";错误原因:" + e.getMessage());
+ }
+ }
+
+ /**
+ * 腾讯云文档中心==》对象存储==》JAVA SDK===> 删除对象
+ * https://cloud.tencent.com/document/product/436/10199#.E5.88.A0.E9.99.A4.E5.AF.B9.E8.B1.A1
+ */
+ @Override
+ public boolean deleteFile(String fileUrl) {
+ if (StringUtils.isBlank(config.getBucketName())) {
+ throw new CustomException("tencent cos bucket name 不能为空,请检查!");
+ }
+
+ // 指定被删除的文件在 COS 上的路径,即对象键。例如对象键为folder/picture.jpg,则表示删除位于 folder 路径下的文件 picture.jpg
+ String key = this.getStorePath(fileUrl);
+ try {
+ cosClient.deleteObject(config.getBucketName(), key);
+ } catch (CosClientException e) {
+ e.printStackTrace();
+ throw new CustomException("tencent cos 删除重新异常,请排查...");
+ }
+ return true;
+ }
+
+ /**
+ * 腾讯云文档中心==》对象存储==》JAVA SDK===> 查询对象列表
+ * 无法直接获取所有文件大小
+ * https://cloud.tencent.com/document/product/436/10199#.E6.9F.A5.E8.AF.A2.E5.AF.B9.E8.B1.A1.E5.88.97.E8.A1.A8
+ */
+ @Override
+ public String objectsCapacityStr() {
+ if (StringUtils.isBlank(config.getBucketName())) {
+ throw new CustomException("tencent cos bucket name 不能为空,请检查!");
+ }
+
+ // Bucket的命名格式为 BucketName-APPID ,此处填写的存储桶名称必须为此格式
+ String bucketName = config.getBucketName();
+ ListObjectsRequest listObjectsRequest = new ListObjectsRequest();
+ // 设置bucket名称
+ listObjectsRequest.setBucketName(bucketName);
+ /// prefix表示列出的object的key以prefix开始
+ ///listObjectsRequest.setPrefix("images/");
+ /// deliter表示分隔符, 设置为/表示列出当前目录下的object, 设置为空表示列出所有的object
+ ///listObjectsRequest.setDelimiter("/");
+ // 设置最大遍历出多少个对象, 一次listobject最大支持1000
+ listObjectsRequest.setMaxKeys(1000);
+ ObjectListing objectListing = null;
+ long size = 0;
+ do
+ {
+ try {
+ objectListing = cosClient.listObjects(listObjectsRequest);
+ } catch (CosClientException e) {
+ e.printStackTrace();
+ return "";
+ }
+ // common prefix表示表示被delimiter截断的路径, 如delimter设置为/, common prefix则表示所有子目录的路径
+ List
+ * http://qwc2geifw.hn-bkt.clouddn.com/upload/default/header.jpg
+ * ==> upload/default/header.jpg
+ */
+ private String getStorePath(String filePath) {
+ String domain = config.getDomain();
+ String publicPath3 = domain + "/";
+ filePath = filePath.replace(publicPath3, "");
+ return filePath;
+ }
+}
diff --git a/ruoyi-modules/ruoyi-file/src/main/resources/bootstrap.yml b/ruoyi-modules/ruoyi-file/src/main/resources/bootstrap.yml
index 967fa852..4820f729 100644
--- a/ruoyi-modules/ruoyi-file/src/main/resources/bootstrap.yml
+++ b/ruoyi-modules/ruoyi-file/src/main/resources/bootstrap.yml
@@ -76,3 +76,12 @@ qiniu:
bucket-name: guangdong-oss
domain: http://guangdong-oss.ityun.ltd
expiry-duration: 32400
+
+# 文件服务器之7 tencent cos 文件存储
+tencent-cos:
+ access-key: AKIDX9hNAzpdUI0XyRpASj098xa7uYzOekmh
+ secret-key: sW5VgkdHlDYqy01xiGbkjV5TghUEvYEw
+ endpoint: ap-chengdu
+ bucket-name: tencent-cloud-cos-dazer-1253883700
+ domain: https://cos.ityun.ltd
+ expiry-duration: 32400