This commit is contained in:
Vinjor 2025-06-20 17:43:53 +08:00
parent a54657c88d
commit d7cfd8598d
13 changed files with 280 additions and 261 deletions

View File

@ -37,6 +37,8 @@
<spring-framework.version>5.3.39</spring-framework.version>
<hutool.version>5.8.26</hutool.version>
<mybatis-plus.version>3.5.1</mybatis-plus.version>
<aliyun.sdk.oss.version>3.17.4</aliyun.sdk.oss.version>
<httpclient.version>4.5.13</httpclient.version>
</properties>
<!-- 依赖声明 -->
@ -229,7 +231,18 @@
<artifactId>ruoyi-common</artifactId>
<version>${ruoyi.version}</version>
</dependency>
<!-- 阿里云OSS-->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>${aliyun.sdk.oss.version}</version>
</dependency>
<!-- 简化HTTP请求处理 -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>${httpclient.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

View File

@ -16,6 +16,18 @@
</description>
<dependencies>
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.17.4</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<!--微信支付SDK-->
<dependency>
@ -106,7 +118,16 @@
<artifactId>weixin-java-common</artifactId>
<version>4.6.0</version>
</dependency>
<!-- 阿里云OSS-->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
</dependency>
<!-- 简化HTTP请求处理 -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
</dependencies>
<build>

View File

@ -2,8 +2,10 @@ package com.ruoyi.ueditorConfig;
import com.alibaba.fastjson2.JSON;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.file.FileUploadUtils;
import com.ruoyi.framework.config.ServerConfig;
import com.ruoyi.web.controller.common.CommonController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@ -29,9 +31,11 @@ public class UeditorController {
@Autowired
private ServerConfig serverConfig;
@Autowired
private CommonController commonController;
@RequestMapping(value = "/exec")
public String config(HttpServletRequest request, HttpServletResponse response, String action, MultipartFile[] upfile) throws IOException {
public String config(HttpServletRequest request, HttpServletResponse response, String action, MultipartFile[] upfile) throws Exception {
if (action.equals("config")) {
request.setCharacterEncoding("utf-8");
response.setHeader("Content-Type", "text/html");
@ -43,42 +47,33 @@ public class UeditorController {
} else if (action.equals("uploadimage")) {
Map<String, Object> result = new HashMap<String, Object>();
for (MultipartFile multipartFile : upfile) {
String upload = FileUploadUtils.upload(RuoYiConfig.getUploadPath(),multipartFile);
System.out.println("upload = " + upload);
System.out.println("upload.sub= " + upload.substring(upload.lastIndexOf("/")));
result.put("title", upload.substring(upload.lastIndexOf("/")+1));
AjaxResult ajaxResult = commonController.uploadFile(multipartFile);
result.put("title", multipartFile.getOriginalFilename());
result.put("original", multipartFile.getOriginalFilename());
result.put("state", "SUCCESS");
result.put("url", serverConfig.getUrl()+upload);
String jStr = JSON.toJSONString(result);
return jStr;
result.put("url", ajaxResult.get("url"));
return JSON.toJSONString(result);
}
return null;
}else if(action.equals("uploadvideo")){
Map<String, Object> result = new HashMap<String, Object>();
for (MultipartFile multipartFile : upfile) {
String upload = FileUploadUtils.upload(RuoYiConfig.getUploadPath(),multipartFile);
System.out.println("upload = " + upload);
System.out.println("upload.sub = " + upload.substring(upload.lastIndexOf("/")));
result.put("title", upload.substring(upload.lastIndexOf("/")+1));
AjaxResult ajaxResult = commonController.uploadFile(multipartFile);
result.put("title", multipartFile.getOriginalFilename());
result.put("original", multipartFile.getOriginalFilename());
result.put("state", "SUCCESS");
result.put("url", serverConfig.getUrl()+upload);
String jStr = JSON.toJSONString(result);
return jStr;
result.put("url", ajaxResult.get("url"));
return JSON.toJSONString(result);
}
}else{
Map<String, Object> result = new HashMap<String, Object>();
for (MultipartFile multipartFile : upfile) {
String upload = FileUploadUtils.upload(RuoYiConfig.getUploadPath(),multipartFile);
System.out.println("upload = " + upload);
System.out.println("upload.sub= " + upload.substring(upload.lastIndexOf("/")));
result.put("title", upload.substring(upload.lastIndexOf("/")+1));
AjaxResult ajaxResult = commonController.uploadFile(multipartFile);
result.put("title", multipartFile.getOriginalFilename());
result.put("original", multipartFile.getOriginalFilename());
result.put("state", "SUCCESS");
result.put("url", serverConfig.getUrl()+upload);
String jStr = JSON.toJSONString(result);
return jStr;
result.put("url", ajaxResult.get("url"));
return JSON.toJSONString(result);
}
}
return null;

View File

@ -0,0 +1,13 @@
package com.ruoyi.upload.service;
import org.springframework.web.multipart.MultipartFile;
public interface FileService {
/**
* 阿里云OSS文件上传
* @param file
* @return
*/
String upload(MultipartFile file);
}

View File

@ -0,0 +1,81 @@
package com.ruoyi.upload.service.impl;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.PutObjectResult;
import com.ruoyi.common.config.OssConfig;
import com.ruoyi.upload.service.FileService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.UUID;
/**
* 文件上传业务类
*
* @author lixiang
*/
@Service
@Slf4j
public class FileServiceImpl implements FileService {
@Autowired
private OssConfig ossConfig;
/**
* 阿里云OSS文件上传
*
* @param file
* @return
*/
@Override
public String upload(MultipartFile file) {
//获取相关配置
String bucketName = ossConfig.getBucketName();
String endPoint = ossConfig.getEndPoint();
String accessKeyId = ossConfig.getAccessKeyId();
String accessKeySecret = ossConfig.getAccessKeySecret();
//创建OSS对象
OSS ossClient = new OSSClientBuilder().build(endPoint, accessKeyId, accessKeySecret);
//获取原生文件名
String originalFilename = file.getOriginalFilename();
//JDK8的日期格式
LocalDateTime time = LocalDateTime.now();
DateTimeFormatter dft = DateTimeFormatter.ofPattern("yyyy/MM/dd");
//拼装OSS上存储的路径
String folder = dft.format(time);
String fileName = generateUUID();
String extension = originalFilename.substring(originalFilename.lastIndexOf("."));
//在OSS上bucket下的文件名
String uploadFileName = "user/" + folder + "/" + fileName + extension;
try {
PutObjectResult result = ossClient.putObject(bucketName, uploadFileName, file.getInputStream());
//拼装返回路径
if (result != null) {
return "https://"+bucketName+"."+endPoint+"/"+uploadFileName;
}
} catch (IOException e) {
log.error("文件上传失败:{}",e.getMessage());
} finally {
//OSS关闭服务不然会造成OOM
ossClient.shutdown();
}
return null;
}
/**
* 获取随机字符串
* @return
*/
private String generateUUID() {
return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
}
}

View File

@ -4,6 +4,9 @@ import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.ruoyi.system.service.ISysConfigService;
import com.ruoyi.upload.service.FileService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@ -34,6 +37,10 @@ public class CommonController
@Autowired
private ServerConfig serverConfig;
@Autowired
private ISysConfigService sysConfigService;
@Autowired
private FileService fileService;
private static final String FILE_DELIMETER = ",";
@ -79,13 +86,22 @@ public class CommonController
{
// 上传文件路径
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));
// 上传并返回新文件名称
String uploadType = sysConfigService.selectConfigByKey("uploadType");
if ("local".equals(uploadType)){
//本地上传方式
String fileName = FileUploadUtils.upload(filePath, file);
String url = serverConfig.getUrl() + fileName;
ajax.put("url", url);
ajax.put("fileName", fileName);
ajax.put("newFileName", FileUtils.getName(fileName));
}else if ("aliOss".equals(uploadType)){
//阿里oss上传方式
String fileUrl = fileService.upload(file);
ajax.put("url", fileUrl);
ajax.put("fileName", fileUrl);
}
ajax.put("originalFilename", file.getOriginalFilename());
return ajax;
}

View File

@ -1,36 +0,0 @@
package com.ruoyi.web.controller.cos;
import com.ruoyi.system.service.CosStsService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.security.PermitAll;
import java.util.Map;
/**
* @Description: sts
* @Author: 86187
* @Date: 2025/03/20 16:34
* @Version: 1.0
*/
@RestController
@RequestMapping("/cos")
@RequiredArgsConstructor
public class CosStsController {
private final CosStsService cosStsService;
/**
* 获取cos临时密钥
*
* @return sts
*/
@GetMapping("/sts")
@PermitAll
public Map<String, Object> getCosSts() {
return cosStsService.getTempKeys();
}
}

View File

@ -143,51 +143,12 @@ xss:
excludes: /system/notice
# 匹配链接
urlPatterns: /system/*,/monitor/*,/tool/*
# 微信小程序配置----多点通告
wx-app:
appId: wxd96fda6510adb6d3
appSecret: 2d3bf7172d09966bd98e1611117c2cb0
token: tgxcxtoken
aesKey: afBZchk5wSFaldDkZB9DZ9Ib6JUV4NwMARaRThsRFqQ
msgDataFormat: XML
wxpay:
#微信公众号appid
appId: wx7d10b0fa4886a583
#商户号
mchId: 1712447936
#商户密匙v2
mchKey: ab94673dd0cca78abd0a453d0aac9f98
# APIv3密钥
apiV3Key: ab94673dd0cca78abd0a453d0aac9f98
#微信支付p12证书
keyPath: D:/任务平台项目/dl_admin/ruoyi-admin/src/main/resources/apiclient_cert.p12
# 微信支付V3-url前缀
baseUrl: https://api.mch.weixin.qq.com/v3
# 支付通知回调, pjm6m9.natappfree.cc 为内网穿透地址
notifyUrl: https://3w823u8516.vicp.fun/noticeApi/payApi/payNotify
# 转账通知回调, pjm6m9.natappfree.cc 为内网穿透地址
zhuanNotifyUrl: https://3w823u8516.vicp.fun/noticeApi/payApi/zhuanNotify
# 退款通知回调, pjm6m9.natappfree.cc 为内网穿透地址
refundNotifyUrl: https://www.ddtg.site/notice/notify/refundNotify
# 密钥路径,resources根目录下
privateKeyPath: D:/任务平台项目/dl_admin/ruoyi-admin/src/main/resources/apiclient_key.pem
privateCertPath: D:/任务平台项目/dl_admin/ruoyi-admin/src/main/resources/apiclient_cert.pem
publicKeyPath: D:/任务平台项目/dl_admin/ruoyi-admin/src/main/resources/pub_key.pem
#商户证书序列号
serialNo: 7FCDB0E72D6A928013361ACB77FA3F0DCBD370E3
publicKeyId: PUB_KEY_ID_0117124479362025041500321584003200
wxAppSecret: 1515e5d055e8dabf5b30faad14ccbc8d
# 阿里云OSS配置
aliyun:
oss:
end-point: oss-cn-qingdao.aliyuncs.com
access-key-id: LTAI5tLThQFWgMLRTf3siNjb
access-key-secret: M5HjOyB8ir5tYEPFOQwImfJNgsumaG
bucket-name: dianliang123
# 普通用户权益值
dl-rights:
# 每月发布通告额度
addNotice: 3
report: 5
#################### 腾讯COS相关配置 ####################
cos:
baseUrl: notice-1348525010.cos.ap-beijing.myqcloud.com
accessKey: AKIDDbyY3Wr9D4i9LK6f085pLfleJlz60hAP
secretKey: 82kJfnu11ulW5TghV5TecVYP3TghXAZl
regionName: ap-beijing
bucketName: notice-1348525010
folderPrefix: /files

View File

@ -0,0 +1,23 @@
package com.ruoyi.common.config;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @description OSS配置类
* @author lixiang
*/
@ConfigurationProperties(prefix = "aliyun.oss")
@Configuration
@Data
public class OssConfig {
private String endPoint;
private String accessKeyId;
private String accessKeySecret;
private String bucketName;
}

View File

@ -0,0 +1,51 @@
//package com.ruoyi.common.utils;
///**
// * 图像上传工具类
// */
//public class AliOssUtil {
//
//
// /**
// * 上传文件到阿里云OSS
// *
// * @param file 要上传的文件
// * @return 返回上传后的文件URL
// * @throws IOException 如果文件处理过程中发生IO异常
// */
// public static String upload(MultipartFile file) throws IOException {
// // 获取文件原始名称
// String origName = file.getOriginalFilename();
// // 获取文件扩展名
// String ext = FilenameUtils.getExtension(origName);
// // 生成UUID作为文件名
// String uuid = UUID.randomUUID().toString().replace("-", "");
// String fileName = uuid + "." + ext;
//
// // 阿里云OSS配置信息
// String endpoint = "https://xxxx.aliyuncs.com";
// String accessKeyId = "xxxxxxxxxxxxxxxxxxxx";
// String accessKeySecret = "xxxxxxxxxxxxxxxxxxxxxxx";
// String bucketName = "仓库名";
//
// // 创建OSS客户端
// OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
//
// try {
// // 将文件上传到OSS
// InputStream is = file.getInputStream();
// ossClient.putObject(bucketName, fileName, is);
// } catch (Exception e) {
// // 异常处理
// e.printStackTrace();
// throw new IOException("上传文件到OSS失败", e);
// } finally {
// // 关闭OSS客户端
// if (ossClient != null) {
// ossClient.shutdown();
// }
// }
//
// // 返回上传后的文件URL
// return ALI_DOMAIN + fileName;
// }
//}

View File

@ -1,82 +0,0 @@
package com.ruoyi.system.service;
import com.tencent.cloud.CosStsClient;
import com.tencent.cloud.Response;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
/**
* @Description: sts
* @Author: 86187
* @Date: 2025/03/20 16:18
* @Version: 1.0
*/
@Service
public class CosStsService {
// 你的腾讯云账号的 SecretId SecretKey
@Value("${cos.accessKey}")
private String SECRET_ID;
@Value("${cos.secretKey}")
private String SECRET_KEY;
// 存储桶所属的地域例如 "ap-shanghai"
@Value("${cos.regionName}")
private String REGION;
// 你的存储桶名例如 "example-1250000000"
@Value("${cos.bucketName}")
private String BUCKET;
// 临时密钥有效期单位秒最长不超过 2 小时7200
private static final int DURATION_SECONDS = 1800;
public Map<String, Object> getTempKeys() {
TreeMap<String, Object> config = new TreeMap<String, Object>();
try {
// api 密钥 SecretId
config.put("secretId", SECRET_ID);
// api 密钥 SecretKey
config.put("secretKey", SECRET_KEY);
// 临时密钥有效时长单位是秒
config.put("durationSeconds", 1800);
// 换成你的 bucket
config.put("bucket", BUCKET);
// 换成 bucket 所在地区
config.put("region", REGION);
// 可以通过 allowPrefixes 指定前缀数组, 例子 a.jpg 或者 a/* 或者 * (使用通配符*存在重大安全风险, 请谨慎评估使用)
config.put("allowPrefixes", new String[] {
"*"
});
// 密钥的权限列表简单上传和分片需要以下的权限其他权限列表请看 https://cloud.tencent.com/document/product/436/31923
String[] allowActions = new String[] {
// 简单上传
"name/cos:PutObject",
"name/cos:PostObject",
// 分片上传
"name/cos:InitiateMultipartUpload",
"name/cos:ListMultipartUploads",
"name/cos:ListParts",
"name/cos:UploadPart",
"name/cos:CompleteMultipartUpload"
};
config.put("allowActions", allowActions);
Response response = CosStsClient.getCredential(config);
Map<String, Object> result= new HashMap<>();
result.put("credentials", response.credentials);
result.put("startTime", response.startTime);
result.put("expiredTime", response.expiredTime);
return result;
} catch (Exception e) {
e.printStackTrace();
throw new IllegalArgumentException("no valid secret !");
}
}
}

View File

@ -2,18 +2,21 @@
<div class="component-upload-image">
<el-upload
multiple
:action="uploadImgUrl"
list-type="picture-card"
:on-success="handleUploadSuccess"
:before-upload="handleBeforeUpload"
:limit="limit"
:on-error="handleUploadError"
:on-exceed="handleExceed"
ref="imageUpload"
:on-remove="handleDelete"
:show-file-list="true"
:headers="headers"
:file-list="fileList"
:on-preview="handlePictureCardPreview"
:disabled="disabled"
:class="{hide: this.fileList.length >= this.limit}"
:http-request="handleHttpRequest"
>
<i class="el-icon-plus"></i>
</el-upload>
@ -41,7 +44,7 @@
</template>
<script>
import cos from '@/utils/cos'; // cos
import { getToken } from "@/utils/auth";
import { isExternal } from "@/utils/validate";
export default {
@ -70,16 +73,6 @@ export default {
isShowTip: {
type: Boolean,
default: true
},
// COS
cosConfig: {
type: Object,
default: () => ({
Bucket: 'notice-1348525010', //
Region: 'ap-beijing', //
Prefix: '/images/' //
}),
required: true
}
},
data() {
@ -89,6 +82,11 @@ export default {
dialogImageUrl: "",
dialogVisible: false,
hideUpload: false,
baseUrl: process.env.VUE_APP_BASE_API,
uploadImgUrl: process.env.VUE_APP_BASE_API + "/common/upload", //
headers: {
Authorization: "Bearer " + getToken(),
},
fileList: []
};
},
@ -101,7 +99,11 @@ export default {
//
this.fileList = list.map(item => {
if (typeof item === "string") {
item = { name: item, url: item };
if (item.indexOf(this.baseUrl) === -1 && !isExternal(item)) {
item = { name: this.baseUrl + item, url: this.baseUrl + item };
} else {
item = { name: item, url: item };
}
}
return item;
});
@ -121,7 +123,7 @@ export default {
},
},
methods: {
//
// loading
handleBeforeUpload(file) {
let isImg = false;
if (this.fileType.length) {
@ -155,62 +157,24 @@ export default {
}
this.$modal.loading("正在上传图片,请稍候...");
this.number++;
return true;
},
//
handleHttpRequest(options) {
const { file } = options;
const fileName = `${this.cosConfig.Prefix}${Date.now()}_${file.name}`;
cos.putObject({
Bucket: this.cosConfig.Bucket,
Region: this.cosConfig.Region,
Key: fileName,
Body: file,
onProgress: (progressData) => {
//
console.log(JSON.stringify(progressData));
}
}, (err, data) => {
if (err) {
this.number--;
this.$modal.closeLoading();
this.$modal.msgError("上传失败");
this.$refs.imageUpload.handleRemove(file);
console.error('上传失败', err);
return;
}
//
const fileUrl = `https://${data.Location}`;
this.handleUploadSuccess({
code: 200,
fileName: file.name,
fileUrl: fileUrl
}, file);
});
},
//
handleExceed() {
this.$modal.msgError(`上传文件数量不能超过 ${this.limit} 个!`);
},
//
handleUploadSuccess(res, file) {
if (res.code === 200) {
this.uploadList.push({ name: res.fileName, url: res.fileUrl });
this.uploadList.push({ name: res.fileName, url: res.fileName });
this.uploadedSuccessfully();
} else {
this.number--;
this.$modal.closeLoading();
this.$modal.msgError(res.msg || "上传失败");
this.$modal.msgError(res.msg);
this.$refs.imageUpload.handleRemove(file);
this.uploadedSuccessfully();
}
},
//
handleDelete(file) {
const findex = this.fileList.map(f => f.name).indexOf(file.name);
@ -219,7 +183,11 @@ export default {
this.$emit("input", this.listToString(this.fileList));
}
},
//
handleUploadError() {
this.$modal.msgError("上传图片失败,请重试");
this.$modal.closeLoading();
},
//
uploadedSuccessfully() {
if (this.number > 0 && this.uploadList.length === this.number) {
@ -230,34 +198,25 @@ export default {
this.$modal.closeLoading();
}
},
//
handlePictureCardPreview(file) {
this.dialogImageUrl = file.url;
this.dialogVisible = true;
},
//
listToString(list, separator) {
let strs = "";
separator = separator || ",";
for (let i in list) {
if (list[i].url) {
strs += list[i].url + separator;
strs += list[i].url.replace(this.baseUrl, "") + separator;
}
}
return strs != '' ? strs.substr(0, strs.length - 1) : '';
},
//
clearFiles() {
this.fileList = [];
this.$emit("input", "");
}
}
};
</script>
<style scoped lang="scss">
// .el-upload--picture-card
::v-deep.hide .el-upload--picture-card {
@ -274,3 +233,5 @@ export default {
transform: translateY(0);
}
</style>

View File

@ -40,7 +40,9 @@ service.interceptors.request.use(config => {
}
if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) {
//拼接当前管理的站点code
config.data.tenantId = getTenantId()
if(config.data){
config.data.tenantId = getTenantId()
}
const requestObj = {
url: config.url,
data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data,