2024-12-30 08:26:40 +08:00
|
|
|
|
import * as tus from "tus-js-client";
|
|
|
|
|
import imageCompression from "browser-image-compression";
|
|
|
|
|
export const uploader = async (
|
|
|
|
|
endpoint: string,
|
|
|
|
|
file: File,
|
|
|
|
|
onProgress?: (percentage: number, speed: number) => void,
|
|
|
|
|
onSuccess?: (url: string) => void,
|
|
|
|
|
onError?: (error: Error) => void
|
|
|
|
|
) => {
|
|
|
|
|
let previousUploadedSize = 0;
|
|
|
|
|
let previousTimestamp = Date.now();
|
|
|
|
|
|
|
|
|
|
// 压缩图像为WebP格式
|
|
|
|
|
const compressImage = async (file: File): Promise<File> => {
|
|
|
|
|
const options = {
|
|
|
|
|
maxSizeMB: 0.8, // 最大文件大小(MB)
|
|
|
|
|
maxWidthOrHeight: 1920, // 最大宽高
|
|
|
|
|
useWebWorker: true,
|
2025-02-21 17:12:47 +08:00
|
|
|
|
filetype: "image/webp", // 输出文件格式
|
2024-12-30 08:26:40 +08:00
|
|
|
|
};
|
|
|
|
|
const compressedFile = await imageCompression(file, options);
|
|
|
|
|
return new File([compressedFile], `${file.name.split(".")[0]}.webp`, {
|
|
|
|
|
type: "image/webp",
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let fileToUpload: File;
|
|
|
|
|
|
|
|
|
|
// 检查并压缩图片文件
|
|
|
|
|
if (file.type.startsWith("image/")) {
|
|
|
|
|
try {
|
|
|
|
|
fileToUpload = await compressImage(file);
|
|
|
|
|
} catch (error: any) {
|
|
|
|
|
console.error("图像压缩失败: " + error.message);
|
|
|
|
|
if (onError) onError(error);
|
|
|
|
|
throw error; // 如果压缩失败,抛出错误并终止上传
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
fileToUpload = file; // 非图片文件,不进行压缩
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const upload = new tus.Upload(fileToUpload, {
|
|
|
|
|
// Replace this with tusd's upload creation URL
|
|
|
|
|
endpoint: `${endpoint}/files/`,
|
|
|
|
|
retryDelays: [0, 3000, 5000, 10000, 20000],
|
|
|
|
|
metadata: {
|
|
|
|
|
filename: fileToUpload.name,
|
|
|
|
|
filetype: fileToUpload.type,
|
|
|
|
|
},
|
|
|
|
|
onError: function (error) {
|
|
|
|
|
console.error("上传失败: " + error.message);
|
|
|
|
|
if (onError) onError(error);
|
|
|
|
|
},
|
|
|
|
|
onProgress: function (bytesUploaded: number, bytesTotal: number) {
|
|
|
|
|
const currentTimestamp = Date.now();
|
|
|
|
|
const timeElapsed = (currentTimestamp - previousTimestamp) / 1000; // in seconds
|
|
|
|
|
const bytesUploadedSinceLastTime =
|
|
|
|
|
bytesUploaded - previousUploadedSize;
|
|
|
|
|
const speed = bytesUploadedSinceLastTime / timeElapsed; // bytes per second
|
|
|
|
|
previousUploadedSize = bytesUploaded;
|
|
|
|
|
previousTimestamp = currentTimestamp;
|
|
|
|
|
const percentage = (bytesUploaded / bytesTotal) * 100;
|
|
|
|
|
if (onProgress) onProgress(percentage, speed);
|
|
|
|
|
},
|
|
|
|
|
onSuccess: function () {
|
|
|
|
|
console.log("上传文件类型", fileToUpload.type);
|
|
|
|
|
console.log("上传文件名称", fileToUpload.name);
|
|
|
|
|
if (onSuccess) onSuccess(upload.url!);
|
|
|
|
|
console.log("Download %s from %s", fileToUpload.name, upload.url);
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Check if there are any previous uploads to continue.
|
|
|
|
|
upload.findPreviousUploads().then(function (previousUploads) {
|
|
|
|
|
// Found previous uploads so we select the first one.
|
|
|
|
|
if (previousUploads && previousUploads.length > 0) {
|
|
|
|
|
upload.resumeFromPreviousUpload(previousUploads[0]!);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return upload;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const uploaderPromise = (
|
|
|
|
|
endpoint: string,
|
|
|
|
|
file: File,
|
|
|
|
|
onProgress?: (percentage: number, speed: number) => void
|
|
|
|
|
): Promise<string> => {
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
uploader(endpoint, file, onProgress, resolve, reject)
|
|
|
|
|
.then((upload) => {
|
|
|
|
|
upload.start();
|
|
|
|
|
})
|
|
|
|
|
.catch(reject);
|
|
|
|
|
});
|
|
|
|
|
};
|