import { useState } from 'react'; import * as tus from 'tus-js-client'; interface UploadResult { compressedUrl: string; url: string; fileId: string; fileName: string; } export function useTusUpload() { const [uploadProgress, setUploadProgress] = useState(0); const [isUploading, setIsUploading] = useState(false); const [uploadError, setUploadError] = useState(null); // 获取服务器配置 const getServerUrl = () => { const ip = process.env.NEXT_PUBLIC_SERVER_IP || 'http://localhost'; const port = process.env.NEXT_PUBLIC_SERVER_PORT || '3000'; return `${ip}:${port}`; }; // 文件上传函数 const handleFileUpload = ( file: File, onSuccess?: (result: UploadResult) => void, onError?: (error: string) => void, ): Promise => { 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], metadata: { filename: file.name, filetype: file.type, }, onError: (error) => { console.error('Upload failed:', error); const errorMessage = error.message || 'Upload failed'; setUploadError(errorMessage); setIsUploading(false); onError?.(errorMessage); reject(new Error(errorMessage)); }, onProgress: (bytesUploaded, bytesTotal) => { const percentage = Math.round((bytesUploaded / bytesTotal) * 100); setUploadProgress(percentage); }, onSuccess: () => { console.log('Upload completed successfully'); setIsUploading(false); 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); }, }); // 开始上传 upload.start(); }); }; // 根据 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; } catch (error) { console.error('Failed to get upload status:', error); return null; } }; return { uploadProgress, isUploading, uploadError, handleFileUpload, getFileUrlByFileId, getFileInfo, getUploadStatus, serverUrl: getServerUrl(), }; }