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

224 lines
5.2 KiB
TypeScript
Raw Normal View History

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);
});
}