import { getAliyunKeyApi, insertFileApi } from "@/service/file"; import { Message } from "@arco-design/web-vue"; import { showUploadProgress, updateUploadProgress, closeUploadProgress } from "@/components/upload-progress/index"; export interface UploadImageResponse { name: string; size: string; url: string; blob: Blob; } export const uploadImage = ( options: { quality?: number; mimeType?: string; } = {} ): Promise => { const { quality = 0.8, mimeType = "image/webp" } = options; return new Promise((resolve, reject) => { if (typeof document === "undefined") { reject(new Error("Require browser environment")); return; } const input = document.createElement("input"); input.type = "file"; input.accept = "image/*"; input.style.display = "none"; const processImage = (file: File): Promise => { return new Promise((resolve, reject) => { const img = new Image(); const objectURL = URL.createObjectURL(file); img.onload = () => { try { const canvas = document.createElement("canvas"); const ctx = canvas.getContext("2d"); if (!ctx) throw new Error("Canvas init failed"); canvas.width = img.width; canvas.height = img.height; ctx.drawImage(img, 0, 0, canvas.width, canvas.height); URL.revokeObjectURL(objectURL); canvas.toBlob( (blob) => { if (!blob) { reject(new Error("Compression failed")); return; } resolve({ name: file.name, size: blob.size.toString(), url: URL.createObjectURL(blob), blob: blob }); }, mimeType, quality ); } catch (error) { reject(error); } }; img.onerror = () => { URL.revokeObjectURL(objectURL); reject(new Error("Image load failed")); }; img.src = objectURL; }); }; input.onchange = async (event) => { const file = (event.target as HTMLInputElement).files?.[0]; if (!file) { reject(new Error("No file selected")); return; } try { if (!file.type.startsWith("image/")) { throw new Error("Invalid image file"); } const result = await processImage(file); resolve(result); } catch (error) { reject(error); } finally { document.body.removeChild(input); } }; document.body.appendChild(input); input.click(); }); }; export interface UploadVideoResponse { name: string; size: string; url: string; blob: Blob; } export const uploadVideo = (): Promise => { return new Promise((resolve, reject) => { if (typeof document === "undefined") { reject(new Error("Require browser environment")); return; } const input = document.createElement("input"); input.type = "file"; input.accept = "video/*"; input.style.display = "none"; input.onchange = (event) => { const file = (event.target as HTMLInputElement).files?.[0]; if (!file) { reject(new Error("No file selected")); return; } if (!file.type.startsWith("video/")) { reject(new Error("Invalid video file")); return; } resolve({ name: file.name, size: file.size.toString(), url: URL.createObjectURL(file), blob: file }); document.body.removeChild(input); }; document.body.appendChild(input); input.click(); }); }; export interface UploadToAliyunResponse { url: string; fileName: string; fileSize: string; } export function uploadToAliyun(blob: Blob, fileName: string, fileSize: string, onProgress?: (percent: number) => void): Promise { return new Promise((resolve, reject) => { // 在开始上传时显示上传进度 showUploadProgress("uploading"); getAliyunKeyApi() .then(async (res) => { try { const aliyunKey = res.data; const formData = new FormData(); const fileExt = fileName.includes(".") ? fileName.slice(fileName.lastIndexOf(".")) : ""; const uniqueName = `sl_${Date.now()}_${Math.random().toString(36).slice(2)}${fileExt}`; formData.append("key", `${aliyunKey.dir}${uniqueName}`); formData.append("OSSAccessKeyId", aliyunKey.accessid); formData.append("policy", aliyunKey.policy); formData.append("Signature", aliyunKey.signature); formData.append("file", blob, uniqueName); const xhr = new XMLHttpRequest(); xhr.open("POST", aliyunKey.host, true); // 监听上传进度 xhr.upload.onprogress = (event) => { if (event.lengthComputable && onProgress) { const percent = (event.loaded / event.total) * 100; onProgress(parseFloat(percent.toFixed(2))); // 保留两位小数 updateUploadProgress(percent); // 更新进度条 } }; xhr.onload = async () => { if (xhr.status === 204 || xhr.status === 200) { const allFilePath = `${aliyunKey.host}/${aliyunKey.dir}${uniqueName}`; await insertFileApi({ fileName, fileSize, fileMapName: allFilePath }); updateUploadProgress(100); // 上传完毕 Message.success("上传成功"); setTimeout(() => { closeUploadProgress(); // 关闭上传进度条 }, 500); resolve({ url: allFilePath, fileName, fileSize }); } else { updateUploadProgress(0, "failed"); // 上传失败 Message.error("上传失败,请稍后再试"); setTimeout(() => { closeUploadProgress(); // 关闭上传进度条 }, 500); reject(new Error(`OSS上传失败: ${xhr.responseText}`)); } }; xhr.onerror = () => { updateUploadProgress(0, "failed"); // 上传失败 Message.error("上传失败,请稍后再试"); setTimeout(() => { closeUploadProgress(); // 关闭上传进度条 }, 500); reject(new Error(`OSS上传异常: ${xhr.responseText}`)); }; xhr.send(formData); } catch (error) { updateUploadProgress(0, "failed"); // 上传失败 Message.error("上传失败,请稍后再试"); setTimeout(() => { closeUploadProgress(); // 关闭上传进度条 }, 500); reject(error); } }) .catch(reject); }); }