2025-05-28 20:00:36 +08:00
|
|
|
|
import { useState } from 'react';
|
|
|
|
|
import * as tus from 'tus-js-client';
|
2025-05-28 08:23:15 +08:00
|
|
|
|
|
|
|
|
|
interface UploadResult {
|
|
|
|
|
compressedUrl: string;
|
|
|
|
|
url: string;
|
|
|
|
|
fileId: string;
|
|
|
|
|
fileName: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function useTusUpload() {
|
2025-05-28 20:00:36 +08:00
|
|
|
|
const [uploadProgress, setUploadProgress] = useState<number>(0);
|
|
|
|
|
const [isUploading, setIsUploading] = useState<boolean>(false);
|
2025-05-28 08:23:15 +08:00
|
|
|
|
const [uploadError, setUploadError] = useState<string | null>(null);
|
|
|
|
|
|
2025-05-28 20:00:36 +08:00
|
|
|
|
// 获取服务器配置
|
|
|
|
|
const getServerUrl = () => {
|
|
|
|
|
const ip = process.env.NEXT_PUBLIC_SERVER_IP || 'http://localhost';
|
|
|
|
|
const port = process.env.NEXT_PUBLIC_SERVER_PORT || '3000';
|
|
|
|
|
return `${ip}:${port}`;
|
2025-05-28 08:23:15 +08:00
|
|
|
|
};
|
|
|
|
|
|
2025-05-28 20:00:36 +08:00
|
|
|
|
// 文件上传函数
|
|
|
|
|
const handleFileUpload = (
|
|
|
|
|
file: File,
|
|
|
|
|
onSuccess?: (result: UploadResult) => void,
|
|
|
|
|
onError?: (error: string) => void,
|
|
|
|
|
): Promise<UploadResult> => {
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
setIsUploading(true);
|
|
|
|
|
setUploadProgress(0);
|
|
|
|
|
setUploadError(null);
|
|
|
|
|
|
|
|
|
|
const serverUrl = getServerUrl();
|
|
|
|
|
const uploadUrl = `${serverUrl}/upload`;
|
|
|
|
|
|
|
|
|
|
const upload = new tus.Upload(file, {
|
|
|
|
|
endpoint: uploadUrl,
|
|
|
|
|
retryDelays: [0, 3000, 5000, 10000, 20000],
|
2025-05-28 08:23:15 +08:00
|
|
|
|
metadata: {
|
2025-05-28 20:00:36 +08:00
|
|
|
|
filename: file.name,
|
|
|
|
|
filetype: file.type,
|
2025-05-28 08:23:15 +08:00
|
|
|
|
},
|
2025-05-28 20:00:36 +08:00
|
|
|
|
onError: (error) => {
|
|
|
|
|
console.error('Upload failed:', error);
|
|
|
|
|
const errorMessage = error.message || 'Upload failed';
|
|
|
|
|
setUploadError(errorMessage);
|
|
|
|
|
setIsUploading(false);
|
|
|
|
|
onError?.(errorMessage);
|
|
|
|
|
reject(new Error(errorMessage));
|
2025-05-28 08:23:15 +08:00
|
|
|
|
},
|
2025-05-28 20:00:36 +08:00
|
|
|
|
onProgress: (bytesUploaded, bytesTotal) => {
|
|
|
|
|
const percentage = Math.round((bytesUploaded / bytesTotal) * 100);
|
|
|
|
|
setUploadProgress(percentage);
|
2025-05-28 08:23:15 +08:00
|
|
|
|
},
|
2025-05-28 20:00:36 +08:00
|
|
|
|
onSuccess: () => {
|
|
|
|
|
console.log('Upload completed successfully');
|
2025-05-28 08:23:15 +08:00
|
|
|
|
setIsUploading(false);
|
2025-05-28 20:00:36 +08:00
|
|
|
|
setUploadProgress(100);
|
|
|
|
|
|
|
|
|
|
// 从上传 URL 中提取目录格式的 fileId
|
|
|
|
|
const uploadUrl = upload.url;
|
|
|
|
|
if (!uploadUrl) {
|
|
|
|
|
const error = 'Failed to get upload URL';
|
|
|
|
|
setUploadError(error);
|
|
|
|
|
onError?.(error);
|
|
|
|
|
reject(new Error(error));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 提取完整的上传ID,然后移除文件名部分得到目录路径
|
|
|
|
|
const fullUploadId = uploadUrl.replace(/^.*\/upload\//, '');
|
|
|
|
|
const fileId = fullUploadId.replace(/\/[^/]+$/, '');
|
|
|
|
|
|
|
|
|
|
console.log('Full upload ID:', fullUploadId);
|
|
|
|
|
console.log('Extracted fileId (directory):', fileId);
|
|
|
|
|
|
|
|
|
|
const result: UploadResult = {
|
|
|
|
|
fileId,
|
|
|
|
|
fileName: file.name,
|
|
|
|
|
url: getFileUrlByFileId(fileId),
|
|
|
|
|
compressedUrl: getFileUrlByFileId(fileId), // 对于简单实现,压缩版本和原版本相同
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
onSuccess?.(result);
|
|
|
|
|
resolve(result);
|
2025-05-28 08:23:15 +08:00
|
|
|
|
},
|
|
|
|
|
});
|
2025-05-28 20:00:36 +08:00
|
|
|
|
|
|
|
|
|
// 开始上传
|
2025-05-28 08:23:15 +08:00
|
|
|
|
upload.start();
|
2025-05-28 20:00:36 +08:00
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 根据 fileId 获取文件访问链接
|
|
|
|
|
const getFileUrlByFileId = (fileId: string): string => {
|
|
|
|
|
const serverUrl = getServerUrl();
|
|
|
|
|
// 对fileId进行URL编码以处理其中的斜杠
|
|
|
|
|
const encodedFileId = encodeURIComponent(fileId);
|
|
|
|
|
return `${serverUrl}/download/${encodedFileId}`;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 检查文件是否存在并获取详细信息
|
|
|
|
|
const getFileInfo = async (fileId: string) => {
|
|
|
|
|
try {
|
|
|
|
|
const serverUrl = getServerUrl();
|
|
|
|
|
// 对fileId进行URL编码以处理其中的斜杠
|
|
|
|
|
const encodedFileId = encodeURIComponent(fileId);
|
|
|
|
|
const response = await fetch(`${serverUrl}/api/storage/resource/${encodedFileId}`);
|
|
|
|
|
const data = await response.json();
|
|
|
|
|
|
|
|
|
|
if (data.status === 'UPLOADED' && data.resource) {
|
|
|
|
|
return {
|
|
|
|
|
...data.resource,
|
|
|
|
|
url: getFileUrlByFileId(fileId),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('File info response:', data);
|
|
|
|
|
return null;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Failed to get file info:', error);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 获取上传状态
|
|
|
|
|
const getUploadStatus = async (fileId: string) => {
|
|
|
|
|
try {
|
|
|
|
|
const serverUrl = getServerUrl();
|
|
|
|
|
const response = await fetch(`${serverUrl}/upload/${fileId}`, {
|
|
|
|
|
method: 'HEAD',
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (response.status === 200) {
|
|
|
|
|
const uploadLength = response.headers.get('Upload-Length');
|
|
|
|
|
const uploadOffset = response.headers.get('Upload-Offset');
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
isComplete: uploadLength === uploadOffset,
|
|
|
|
|
progress:
|
|
|
|
|
uploadLength && uploadOffset ? Math.round((parseInt(uploadOffset) / parseInt(uploadLength)) * 100) : 0,
|
|
|
|
|
uploadLength: uploadLength ? parseInt(uploadLength) : 0,
|
|
|
|
|
uploadOffset: uploadOffset ? parseInt(uploadOffset) : 0,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
2025-05-28 08:23:15 +08:00
|
|
|
|
} catch (error) {
|
2025-05-28 20:00:36 +08:00
|
|
|
|
console.error('Failed to get upload status:', error);
|
|
|
|
|
return null;
|
2025-05-28 08:23:15 +08:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
uploadProgress,
|
|
|
|
|
isUploading,
|
|
|
|
|
uploadError,
|
|
|
|
|
handleFileUpload,
|
2025-05-28 20:00:36 +08:00
|
|
|
|
getFileUrlByFileId,
|
|
|
|
|
getFileInfo,
|
|
|
|
|
getUploadStatus,
|
|
|
|
|
serverUrl: getServerUrl(),
|
2025-05-28 08:23:15 +08:00
|
|
|
|
};
|
|
|
|
|
}
|