fenghuo/apps/web/hooks/useTusUpload.ts

166 lines
4.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<number>(0);
const [isUploading, setIsUploading] = useState<boolean>(false);
const [uploadError, setUploadError] = useState<string | null>(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<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],
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(),
};
}