import { ElMessage } from "element-plus"; import { uploadImage, uploadDocument, uploadFile } from "@/api/upload"; /** * 文件大小格式化 * @param bytes 字节数 * @returns 格式化后的字符串 */ export function formatFileSize(bytes: number): string { if (bytes === 0) return "0 B"; const k = 1024; const sizes = ["B", "KB", "MB", "GB", "TB"]; const i = Math.floor(Math.log(bytes) / Math.log(k)); return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + " " + sizes[i]; } /** * 获取文件扩展名 * @param filename 文件名 * @returns 扩展名(小写) */ export function getFileExtension(filename: string): string { return filename .slice(((filename.lastIndexOf(".") - 1) >>> 0) + 2) .toLowerCase(); } /** * 验证文件类型 * @param file 文件对象 * @param allowedTypes 允许的类型数组 * @returns 是否通过验证 */ export function validateFileType(file: File, allowedTypes: string[]): boolean { return allowedTypes.includes(file.type); } /** * 验证文件大小 * @param file 文件对象 * @param maxSizeMB 最大大小(MB) * @returns 是否通过验证 */ export function validateFileSize(file: File, maxSizeMB: number): boolean { return file.size <= maxSizeMB * 1024 * 1024; } /** * 通用上传处理函数 * @param file 文件对象 * @param options 配置选项 * @returns Promise 返回文件 URL */ export interface UploadOptions { type?: "image" | "document" | "file"; maxSize?: number; allowedTypes?: string[]; onProgress?: (progress: number) => void; onSuccess?: (url: string) => void; onError?: (error: Error) => void; } export async function handleUpload( file: File, options: UploadOptions = {} ): Promise { const { type = "file", maxSize, allowedTypes, onProgress, onSuccess, onError, } = options; try { // 进度回调包装 const progressCallback = onProgress ? (progressEvent: any) => { const progress = Math.round( (progressEvent.loaded * 100) / progressEvent.total ); onProgress(progress); } : undefined; let res; // 根据类型选择上传方法 switch (type) { case "image": res = await uploadImage(file, { maxSize, allowedTypes, onProgress: progressCallback, }); break; case "document": res = await uploadDocument(file, { maxSize, allowedTypes, onProgress: progressCallback, }); break; default: res = await uploadFile(file, progressCallback); } const url = res.data.url; if (onSuccess) { onSuccess(url); } return url; } catch (error: any) { if (onError) { onError(error); } else { ElMessage.error(error.message || "上传失败"); } throw error; } } /** * 图片压缩 * @param file 图片文件 * @param maxWidth 最大宽度 * @param quality 压缩质量 0-1 * @returns Promise 压缩后的文件 */ export function compressImage( file: File, maxWidth = 1920, quality = 0.8 ): Promise { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.readAsDataURL(file); reader.onload = (e) => { const img = new Image(); img.src = e.target?.result as string; img.onload = () => { const canvas = document.createElement("canvas"); let width = img.width; let height = img.height; // 计算缩放比例 if (width > maxWidth) { height = (height * maxWidth) / width; width = maxWidth; } canvas.width = width; canvas.height = height; const ctx = canvas.getContext("2d"); ctx?.drawImage(img, 0, 0, width, height); canvas.toBlob( (blob) => { if (blob) { const compressedFile = new File([blob], file.name, { type: file.type, lastModified: Date.now(), }); resolve(compressedFile); } else { reject(new Error("图片压缩失败")); } }, file.type, quality ); }; img.onerror = () => { reject(new Error("图片加载失败")); }; }; reader.onerror = () => { reject(new Error("文件读取失败")); }; }); } /** * Base64 转 File * @param base64 base64 字符串 * @param filename 文件名 * @returns File 对象 */ export function base64ToFile(base64: string, filename: string): File { const arr = base64.split(","); const mime = arr[0].match(/:(.*?);/)?.[1] || "image/png"; const bstr = atob(arr[1]); let n = bstr.length; const u8arr = new Uint8Array(n); while (n--) { u8arr[n] = bstr.charCodeAt(n); } return new File([u8arr], filename, { type: mime }); } /** * File 转 Base64 * @param file 文件对象 * @returns Promise base64 字符串 */ export function fileToBase64(file: File): Promise { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.readAsDataURL(file); reader.onload = () => resolve(reader.result as string); reader.onerror = (error) => reject(error); }); }