Просмотр исходного кода

oss配置调整与优化
会计、中审报销批量上传功能修复

徐滕 3 дней назад
Родитель
Сommit
b088c8b8b5
1 измененных файлов с 77 добавлено и 8 удалено
  1. 77 8
      src/api/sys/OSSService.js

+ 77 - 8
src/api/sys/OSSService.js

@@ -119,14 +119,73 @@ export default class OSSSerive {
 	}
 }
 
-export const client = new OSS({
-	region: "oss-cn-hangzhou", // oss地址
-	accessKeyId: "LTAI5tKa6kzGr5EyPWJB4EcD", // 通过阿里云控制台创建的AccessKey ID。
-	accessKeySecret: "arHxB7ZPhizrBYf4844TtyaRctPMgW", // 通过阿里云控制台创建的AccessKey Secret。
-	bucket: import.meta.env.VITE_OSS_BUCKET, // 仓库名字:由 .env 决定(dev=xgxm-test / build=xg-pg),勿在此硬编码切换
-	useFetch: true, // 支持上传大于100KB的文件
-	secure: false, // 返回的url为https
-});
+// OSS 客户端改用 STS 临时凭证:不再硬编码永久 AccessKey。
+// 上传前向后端 /oss/oss/getAccess 换取限时凭证,缓存复用;
+// 大文件分片上传期间由 SDK 的 refreshSTSToken 按间隔自动续期,避免临期上传中断。
+
+// 换取并校验 STS 临时凭证(status=200 且凭证非空才返回)
+async function fetchStsToken() {
+	const res = await request({
+		url: prefix + "/oss/oss/getAccess",
+		method: "post",
+	});
+	if (
+		!res ||
+		String(res.status) !== "200" ||
+		!res.AccessKeyId ||
+		!res.SecurityToken
+	) {
+		throw new Error(
+			"获取OSS临时凭证失败" +
+				(res && res.status ? "(status=" + res.status + ")" : "")
+		);
+	}
+	return res;
+}
+
+let ossClientCache = null; // { client, expireAt }
+let ossClientPending = null; // 并发上传时共用一次凭证请求,避免重复换取
+
+async function getOssClient() {
+	const now = Date.now();
+	// 凭证提前 5 分钟刷新,避免拿到临期凭证
+	if (ossClientCache && now < ossClientCache.expireAt - 5 * 60 * 1000) {
+		return ossClientCache.client;
+	}
+	if (!ossClientPending) {
+		ossClientPending = fetchStsToken()
+			.then((res) => {
+				const expireAt = res.Expiration
+					? new Date(res.Expiration).getTime()
+					: Date.now() + 60 * 60 * 1000;
+				const ossClient = new OSS({
+					region: "oss-cn-hangzhou", // oss地址
+					accessKeyId: res.AccessKeyId, // STS 临时 AccessKeyId
+					accessKeySecret: res.AccessKeySecret, // STS 临时 AccessKeySecret
+					stsToken: res.SecurityToken, // STS 安全令牌,临时凭证必带
+					bucket: res.Bucket || import.meta.env.VITE_OSS_BUCKET, // 优先后端返回的桶(随环境),兜底 .env
+					useFetch: true, // 支持上传大于100KB的文件
+					secure: false, // 与原有行为保持一致
+					// 每个请求前 SDK 按间隔自动续期凭证,保障超大文件长时间分片上传不中断
+					refreshSTSToken: async () => {
+						const r = await fetchStsToken();
+						return {
+							accessKeyId: r.AccessKeyId,
+							accessKeySecret: r.AccessKeySecret,
+							stsToken: r.SecurityToken,
+						};
+					},
+					refreshSTSTokenInterval: 5 * 60 * 1000, // 续期间隔 5 分钟,远小于凭证 1 小时有效期
+				});
+				ossClientCache = { client: ossClient, expireAt };
+				return ossClient;
+			})
+			.finally(() => {
+				ossClientPending = null;
+			});
+	}
+	return ossClientPending;
+}
 
 export const headers = {
 	// 指定该Object被下载时网页的缓存行为。
@@ -286,6 +345,16 @@ export async function httpRequest(file, name, type, maxValue) {
 	const fileName = filePath + name + "." + exname;
 	// console.log(fileName, '文件名')
 
+	// 获取 OSS 临时凭证客户端(STS,过期自动刷新);失败按上传失败处理
+	let client;
+	try {
+		client = await getOssClient();
+	} catch (err) {
+		console.log("上传失败", err);
+		file.onError("上传失败");
+		return;
+	}
+
 	await client
 		.multipartUpload(fileName, file.file, {
 			// eslint-disable-next-line no-unused-vars