import OSS from "ali-oss"; import { http } from "@/utils/http"; interface OssCredential { accessKeyId: string; accessKeySecret: string; securityToken: string; expiration: string; host: string; dir: string; } export interface UploadResult { url: string; objectKey: string; fileName: string; fileSize: number; fileExtension: string; } let cachedCredential: OssCredential | null = null; let credentialExpireTime = 0; /** * 获取 OSS 上传凭证(带缓存,过期前60秒自动刷新) */ async function getOssCredential(dir: string): Promise { const now = Date.now(); if (cachedCredential && credentialExpireTime - now > 60000) { return cachedCredential; } const res = await http.request<{ code: number; message: string; data: OssCredential; }>("get", "/oss/upload-credential", { params: { dir } }); if (res.code !== 200) { throw new Error(res.message || "获取上传凭证失败"); } cachedCredential = res.data; credentialExpireTime = new Date(res.data.expiration).getTime(); return cachedCredential; } /** * 前端直传文件到 OSS * * @param file 待上传文件 * @param dir 上传目录(如 "shop-archive/contract") * @param onProgress 上传进度回调 (percent: number) => void * @returns 上传结果(URL、objectKey等) */ export async function uploadToOss( file: File, dir: string = "shop-archive", onProgress?: (percent: number) => void ): Promise { const credential = await getOssCredential(dir); // 从 host 中提取 region 和 bucket const hostMatch = credential.host.match( /\/\/(.+?)\.(oss-.+?)\.aliyuncs\.com/ ); const bucket = hostMatch?.[1] || ""; const region = hostMatch?.[2] || "oss-cn-hangzhou"; const client = new OSS({ region, accessKeyId: credential.accessKeyId, accessKeySecret: credential.accessKeySecret, stsToken: credential.securityToken, bucket, endpoint: credential.host }); // 生成 objectKey:dir/yyyyMMdd/uuid.ext const ext = file.name.includes(".") ? file.name.substring(file.name.lastIndexOf(".")) : ""; const dateStr = new Date() .toISOString() .slice(0, 10) .replace(/-/g, ""); const uuid = Math.random().toString(36).substring(2, 15) + Date.now().toString(36); const objectKey = `${credential.dir}${dateStr}/${uuid}${ext}`; await client.put(objectKey, file, { progress: (p: number) => onProgress?.(Math.round(p * 100)) }); // 拼接完整访问URL const url = `${credential.host.replace(/\/$/, "")}/${objectKey}`; return { url, objectKey, fileName: file.name, fileSize: file.size, fileExtension: ext.replace(".", "") }; }