123 lines
3.5 KiB
TypeScript
123 lines
3.5 KiB
TypeScript
import { useState } from "react";
|
||
import * as tus from "tus-js-client";
|
||
import { env } from "../env";
|
||
import { getCompressedImageUrl } from "@nice/utils";
|
||
|
||
interface UploadResult {
|
||
compressedUrl: string;
|
||
url: string;
|
||
fileId: string;
|
||
fileName: string;
|
||
}
|
||
|
||
export function useTusUpload() {
|
||
const [uploadProgress, setUploadProgress] = useState<
|
||
Record<string, number>
|
||
>({});
|
||
const [isUploading, setIsUploading] = useState(false);
|
||
const [uploadError, setUploadError] = useState<string | null>(null);
|
||
|
||
const getFileId = (url: string) => {
|
||
const parts = url.split("/");
|
||
const uploadIndex = parts.findIndex((part) => part === "upload");
|
||
if (uploadIndex === -1 || uploadIndex + 4 >= parts.length) {
|
||
throw new Error("Invalid upload URL format");
|
||
}
|
||
return parts.slice(uploadIndex + 1, uploadIndex + 5).join("/");
|
||
};
|
||
const getResourceUrl = (url: string) => {
|
||
const parts = url.split("/");
|
||
const uploadIndex = parts.findIndex((part) => part === "upload");
|
||
if (uploadIndex === -1 || uploadIndex + 4 >= parts.length) {
|
||
throw new Error("Invalid upload URL format");
|
||
}
|
||
const resUrl = `http://${env.SERVER_IP}:${env.FILE_PORT}/uploads/${parts.slice(uploadIndex + 1, uploadIndex + 6).join("/")}`;
|
||
return resUrl;
|
||
};
|
||
const handleFileUpload = async (
|
||
file: File | Blob,
|
||
onSuccess: (result: UploadResult) => void,
|
||
onError: (error: Error) => void,
|
||
fileKey: string // 添加文件唯一标识
|
||
) => {
|
||
// console.log()
|
||
setIsUploading(true);
|
||
setUploadProgress((prev) => ({ ...prev, [fileKey]: 0 }));
|
||
setUploadError(null);
|
||
|
||
try {
|
||
// 如果是Blob,需要转换为File
|
||
let fileName = "uploaded-file";
|
||
if (file instanceof Blob && !(file instanceof File)) {
|
||
// 根据MIME类型设置文件扩展名
|
||
const extension = file.type.split('/')[1];
|
||
fileName = `uploaded-file.${extension}`;
|
||
}
|
||
const uploadFile = file instanceof Blob && !(file instanceof File)
|
||
? new File([file], fileName, { type: file.type })
|
||
: file as File;
|
||
console.log(`http://${env.SERVER_IP}:${env.SERVER_PORT}/upload`);
|
||
const upload = new tus.Upload(uploadFile, {
|
||
endpoint: `http://${env.SERVER_IP}:${env.SERVER_PORT}/upload`,
|
||
retryDelays: [0, 1000, 3000, 5000],
|
||
metadata: {
|
||
filename: uploadFile.name,
|
||
filetype: uploadFile.type,
|
||
size: uploadFile.size as any,
|
||
},
|
||
onProgress: (bytesUploaded, bytesTotal) => {
|
||
const progress = Number(
|
||
((bytesUploaded / bytesTotal) * 100).toFixed(2)
|
||
);
|
||
setUploadProgress((prev) => ({
|
||
...prev,
|
||
[fileKey]: progress,
|
||
}));
|
||
},
|
||
onSuccess: async (payload) => {
|
||
if (upload.url) {
|
||
const fileId = getFileId(upload.url);
|
||
//console.log(fileId)
|
||
const url = getResourceUrl(upload.url);
|
||
setIsUploading(false);
|
||
setUploadProgress((prev) => ({
|
||
...prev,
|
||
[fileKey]: 100,
|
||
}));
|
||
onSuccess({
|
||
compressedUrl: getCompressedImageUrl(url),
|
||
url,
|
||
fileId,
|
||
fileName: uploadFile.name,
|
||
});
|
||
}
|
||
},
|
||
onError: (error) => {
|
||
const err =
|
||
error instanceof Error
|
||
? error
|
||
: new Error("Unknown error");
|
||
setIsUploading(false);
|
||
setUploadError(error.message);
|
||
console.log(error);
|
||
onError(err);
|
||
},
|
||
});
|
||
upload.start();
|
||
} catch (error) {
|
||
const err =
|
||
error instanceof Error ? error : new Error("Upload failed");
|
||
setIsUploading(false);
|
||
setUploadError(err.message);
|
||
onError(err);
|
||
}
|
||
};
|
||
|
||
return {
|
||
uploadProgress,
|
||
isUploading,
|
||
uploadError,
|
||
handleFileUpload,
|
||
};
|
||
}
|