158 lines
4.7 KiB
TypeScript
158 lines
4.7 KiB
TypeScript
![]() |
import React, { useState } from 'react';
|
|||
|
import { useTusUpload } from '../hooks/useTusUpload';
|
|||
|
|
|||
|
interface FileDownloadProps {
|
|||
|
fileId?: string;
|
|||
|
fileName?: string;
|
|||
|
className?: string;
|
|||
|
}
|
|||
|
|
|||
|
export function FileDownload({ fileId, fileName, className }: FileDownloadProps) {
|
|||
|
const { getFileUrlByFileId, getFileInfo } = useTusUpload();
|
|||
|
const [inputFileId, setInputFileId] = useState(fileId || '');
|
|||
|
const [fileInfo, setFileInfo] = useState<any>(null);
|
|||
|
const [loading, setLoading] = useState(false);
|
|||
|
const [error, setError] = useState<string | null>(null);
|
|||
|
|
|||
|
// 获取文件信息
|
|||
|
const handleGetFileInfo = async () => {
|
|||
|
if (!inputFileId.trim()) {
|
|||
|
setError('请输入文件ID');
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
setLoading(true);
|
|||
|
setError(null);
|
|||
|
|
|||
|
try {
|
|||
|
const info = await getFileInfo(inputFileId);
|
|||
|
if (info) {
|
|||
|
setFileInfo(info);
|
|||
|
} else {
|
|||
|
setError('文件不存在或未准备好');
|
|||
|
}
|
|||
|
} catch (err) {
|
|||
|
setError('获取文件信息失败');
|
|||
|
} finally {
|
|||
|
setLoading(false);
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
// 直接下载文件
|
|||
|
const handleDirectDownload = () => {
|
|||
|
const downloadUrl = getFileUrlByFileId(inputFileId);
|
|||
|
window.open(downloadUrl, '_blank');
|
|||
|
};
|
|||
|
|
|||
|
// 复制下载链接
|
|||
|
const handleCopyLink = async () => {
|
|||
|
const downloadUrl = getFileUrlByFileId(inputFileId);
|
|||
|
try {
|
|||
|
await navigator.clipboard.writeText(downloadUrl);
|
|||
|
alert('下载链接已复制到剪贴板!');
|
|||
|
} catch (error) {
|
|||
|
console.error('复制失败:', error);
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
// 在新窗口预览文件
|
|||
|
const handlePreview = () => {
|
|||
|
const downloadUrl = getFileUrlByFileId(inputFileId);
|
|||
|
window.open(downloadUrl, '_blank');
|
|||
|
};
|
|||
|
|
|||
|
return (
|
|||
|
<div className={`p-6 bg-white rounded-lg shadow-md ${className || ''}`}>
|
|||
|
<h3 className="text-lg font-semibold mb-4">文件下载</h3>
|
|||
|
|
|||
|
{/* 文件ID输入 */}
|
|||
|
<div className="mb-4">
|
|||
|
<label className="block text-sm font-medium text-gray-700 mb-2">文件ID</label>
|
|||
|
<div className="flex gap-2">
|
|||
|
<input
|
|||
|
type="text"
|
|||
|
value={inputFileId}
|
|||
|
onChange={(e) => setInputFileId(e.target.value)}
|
|||
|
placeholder="输入文件ID"
|
|||
|
className="flex-1 px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|||
|
/>
|
|||
|
<button
|
|||
|
onClick={handleGetFileInfo}
|
|||
|
disabled={loading}
|
|||
|
className="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 disabled:opacity-50"
|
|||
|
>
|
|||
|
{loading ? '查询中...' : '查询'}
|
|||
|
</button>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
{/* 错误信息 */}
|
|||
|
{error && <div className="mb-4 p-3 bg-red-100 border border-red-300 text-red-700 rounded-md">{error}</div>}
|
|||
|
|
|||
|
{/* 文件信息 */}
|
|||
|
{fileInfo && (
|
|||
|
<div className="mb-6 p-4 bg-gray-50 rounded-md">
|
|||
|
<h4 className="font-medium mb-2">文件信息</h4>
|
|||
|
<div className="space-y-1 text-sm">
|
|||
|
<p>
|
|||
|
<span className="font-medium">文件名:</span> {fileInfo.title || '未知'}
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
<span className="font-medium">类型:</span> {fileInfo.type || '未知'}
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
<span className="font-medium">状态:</span> {fileInfo.status || '未知'}
|
|||
|
</p>
|
|||
|
{fileInfo.meta?.size && (
|
|||
|
<p>
|
|||
|
<span className="font-medium">大小:</span> {formatFileSize(fileInfo.meta.size)}
|
|||
|
</p>
|
|||
|
)}
|
|||
|
<p>
|
|||
|
<span className="font-medium">创建时间:</span> {new Date(fileInfo.createdAt).toLocaleString()}
|
|||
|
</p>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
)}
|
|||
|
|
|||
|
{/* 操作按钮 */}
|
|||
|
{inputFileId && (
|
|||
|
<div className="flex gap-2 flex-wrap">
|
|||
|
<button
|
|||
|
onClick={handleDirectDownload}
|
|||
|
className="px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-700"
|
|||
|
>
|
|||
|
直接下载
|
|||
|
</button>
|
|||
|
<button onClick={handlePreview} className="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700">
|
|||
|
预览/查看
|
|||
|
</button>
|
|||
|
<button onClick={handleCopyLink} className="px-4 py-2 bg-gray-600 text-white rounded-md hover:bg-gray-700">
|
|||
|
复制链接
|
|||
|
</button>
|
|||
|
</div>
|
|||
|
)}
|
|||
|
|
|||
|
{/* 使用说明 */}
|
|||
|
<div className="mt-6 p-4 bg-blue-50 rounded-md">
|
|||
|
<h4 className="text-sm font-medium text-blue-900 mb-2">使用说明:</h4>
|
|||
|
<ul className="text-xs text-blue-700 space-y-1">
|
|||
|
<li>• 输入文件ID后点击"查询"获取文件信息</li>
|
|||
|
<li>• "直接下载"会打开新窗口下载文件</li>
|
|||
|
<li>• "预览/查看"适用于图片、PDF等可预览的文件</li>
|
|||
|
<li>• "复制链接"将下载地址复制到剪贴板</li>
|
|||
|
</ul>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
// 格式化文件大小
|
|||
|
function formatFileSize(bytes: number): string {
|
|||
|
if (bytes === 0) return '0 Bytes';
|
|||
|
const k = 1024;
|
|||
|
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
|||
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|||
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|||
|
}
|