technological-brain-admin/src/utils/upload.ts

224 lines
5.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<string> 返回文件 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<string> {
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<File> 压缩后的文件
*/
export function compressImage(
file: File,
maxWidth = 1920,
quality = 0.8
): Promise<File> {
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<string> base64 字符串
*/
export function fileToBase64(file: File): Promise<string> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result as string);
reader.onerror = (error) => reject(error);
});
}