ossUpload.ts 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. import OSS from "ali-oss";
  2. import { http } from "@/utils/http";
  3. interface OssCredential {
  4. accessKeyId: string;
  5. accessKeySecret: string;
  6. securityToken: string;
  7. expiration: string;
  8. host: string;
  9. dir: string;
  10. }
  11. export interface UploadResult {
  12. url: string;
  13. objectKey: string;
  14. fileName: string;
  15. fileSize: number;
  16. fileExtension: string;
  17. }
  18. let cachedCredential: OssCredential | null = null;
  19. let credentialExpireTime = 0;
  20. /**
  21. * 获取 OSS 上传凭证(带缓存,过期前60秒自动刷新)
  22. */
  23. async function getOssCredential(dir: string): Promise<OssCredential> {
  24. const now = Date.now();
  25. if (cachedCredential && credentialExpireTime - now > 60000) {
  26. return cachedCredential;
  27. }
  28. const res = await http.request<{
  29. code: number;
  30. message: string;
  31. data: OssCredential;
  32. }>("get", "/oss/upload-credential", { params: { dir } });
  33. if (res.code !== 200) {
  34. throw new Error(res.message || "获取上传凭证失败");
  35. }
  36. cachedCredential = res.data;
  37. credentialExpireTime = new Date(res.data.expiration).getTime();
  38. return cachedCredential;
  39. }
  40. /**
  41. * 前端直传文件到 OSS
  42. *
  43. * @param file 待上传文件
  44. * @param dir 上传目录(如 "shop-archive/contract")
  45. * @param onProgress 上传进度回调 (percent: number) => void
  46. * @returns 上传结果(URL、objectKey等)
  47. */
  48. export async function uploadToOss(
  49. file: File,
  50. dir: string = "shop-archive",
  51. onProgress?: (percent: number) => void
  52. ): Promise<UploadResult> {
  53. const credential = await getOssCredential(dir);
  54. // 从 host 中提取 region 和 bucket
  55. const hostMatch = credential.host.match(
  56. /\/\/(.+?)\.(oss-.+?)\.aliyuncs\.com/
  57. );
  58. const bucket = hostMatch?.[1] || "";
  59. const region = hostMatch?.[2] || "oss-cn-hangzhou";
  60. const client = new OSS({
  61. region,
  62. accessKeyId: credential.accessKeyId,
  63. accessKeySecret: credential.accessKeySecret,
  64. stsToken: credential.securityToken,
  65. bucket,
  66. endpoint: credential.host
  67. });
  68. // 生成 objectKey:dir/yyyyMMdd/uuid.ext
  69. const ext = file.name.includes(".")
  70. ? file.name.substring(file.name.lastIndexOf("."))
  71. : "";
  72. const dateStr = new Date()
  73. .toISOString()
  74. .slice(0, 10)
  75. .replace(/-/g, "");
  76. const uuid = Math.random().toString(36).substring(2, 15) + Date.now().toString(36);
  77. const objectKey = `${credential.dir}${dateStr}/${uuid}${ext}`;
  78. await client.put(objectKey, file, {
  79. progress: (p: number) => onProgress?.(Math.round(p * 100))
  80. });
  81. // 拼接完整访问URL
  82. const url = `${credential.host.replace(/\/$/, "")}/${objectKey}`;
  83. return {
  84. url,
  85. objectKey,
  86. fileName: file.name,
  87. fileSize: file.size,
  88. fileExtension: ext.replace(".", "")
  89. };
  90. }