import { useState } from 'react'; import { useTusUpload } from './useTusUpload'; interface DownloadProgress { loaded: number; total: number; percentage: number; } export function useFileDownload() { const { getFileUrlByFileId, serverUrl } = useTusUpload(); const [downloadProgress, setDownloadProgress] = useState(null); const [isDownloading, setIsDownloading] = useState(false); const [downloadError, setDownloadError] = useState(null); // 直接下载文件(浏览器处理) const downloadFile = (fileId: string, filename?: string) => { const url = getFileUrlByFileId(fileId); const link = document.createElement('a'); link.href = url; if (filename) { link.download = filename; } link.target = '_blank'; document.body.appendChild(link); link.click(); document.body.removeChild(link); }; // 带进度的文件下载 const downloadFileWithProgress = async ( fileId: string, filename?: string, onProgress?: (progress: DownloadProgress) => void, ): Promise => { return new Promise(async (resolve, reject) => { setIsDownloading(true); setDownloadError(null); setDownloadProgress(null); try { const url = getFileUrlByFileId(fileId); const response = await fetch(url); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const contentLength = response.headers.get('Content-Length'); const total = contentLength ? parseInt(contentLength, 10) : 0; let loaded = 0; const reader = response.body?.getReader(); if (!reader) { throw new Error('Failed to get response reader'); } const chunks: Uint8Array[] = []; while (true) { const { done, value } = await reader.read(); if (done) break; if (value) { chunks.push(value); loaded += value.length; const progress = { loaded, total, percentage: total > 0 ? Math.round((loaded / total) * 100) : 0, }; setDownloadProgress(progress); onProgress?.(progress); } } // 创建 Blob const blob = new Blob(chunks); // 如果提供了文件名,自动下载 if (filename) { const downloadUrl = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = downloadUrl; link.download = filename; document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(downloadUrl); } setIsDownloading(false); setDownloadProgress(null); resolve(blob); } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Download failed'; setDownloadError(errorMessage); setIsDownloading(false); setDownloadProgress(null); reject(new Error(errorMessage)); } }); }; // 预览文件(在新窗口打开) const previewFile = (fileId: string) => { const url = getFileUrlByFileId(fileId); window.open(url, '_blank', 'noopener,noreferrer'); }; // 获取文件的 Blob URL(用于预览) const getFileBlobUrl = async (fileId: string): Promise => { try { const blob = await downloadFileWithProgress(fileId); return URL.createObjectURL(blob); } catch (error) { throw new Error('Failed to create blob URL'); } }; // 复制文件链接到剪贴板 const copyFileLink = async (fileId: string): Promise => { try { const url = getFileUrlByFileId(fileId); await navigator.clipboard.writeText(url); } catch (error) { throw new Error('Failed to copy link'); } }; // 检查文件是否可以预览(基于 MIME 类型) const canPreview = (mimeType: string): boolean => { const previewableTypes = [ 'image/', // 所有图片 'application/pdf', 'text/', 'video/', 'audio/', ]; return previewableTypes.some((type) => mimeType.startsWith(type)); }; // 获取文件类型图标 const getFileIcon = (mimeType: string): string => { if (mimeType.startsWith('image/')) return '🖼️'; if (mimeType.startsWith('video/')) return '🎥'; if (mimeType.startsWith('audio/')) return '🎵'; if (mimeType === 'application/pdf') return '📄'; if (mimeType.startsWith('text/')) return '📝'; if (mimeType.includes('word')) return '📝'; if (mimeType.includes('excel') || mimeType.includes('spreadsheet')) return '📊'; if (mimeType.includes('powerpoint') || mimeType.includes('presentation')) return '📊'; if (mimeType.includes('zip') || mimeType.includes('rar') || mimeType.includes('archive')) return '📦'; return '📁'; }; return { // 状态 downloadProgress, isDownloading, downloadError, // 方法 downloadFile, downloadFileWithProgress, previewFile, getFileBlobUrl, copyFileLink, // 工具函数 canPreview, getFileIcon, getFileUrlByFileId, serverUrl, }; }