import { buildCosKey, buildPublicUrl } from '@/constants/Cos'; import { uploadWithRetry } from '@/services/cos'; import { useCallback, useMemo, useRef, useState } from 'react'; import { Platform } from 'react-native'; export type UseCosUploadOptions = { prefix?: string; userId?: string; contentType?: string; }; export function useCosUpload(defaultOptions?: UseCosUploadOptions) { const abortRef = useRef(null); const [progress, setProgress] = useState(0); const [uploading, setUploading] = useState(false); const cancel = useCallback(() => { abortRef.current?.abort(); }, []); const upload = useCallback( async (file: { uri?: string; name?: string; type?: string; buffer?: any; blob?: Blob } | Blob | string | any, options?: UseCosUploadOptions) => { const finalOptions = { ...(defaultOptions || {}), ...(options || {}) }; const extGuess = (() => { const name = (file && (file.name || (file as any).filename)) || ''; const match = name.match(/\.([a-zA-Z0-9]+)$/); return match ? match[1] : undefined; })(); const key = buildCosKey({ prefix: finalOptions.prefix, userId: finalOptions.userId, ext: extGuess }); const controller = new AbortController(); abortRef.current = controller; setProgress(0); setUploading(true); try { let res: any; if (Platform.OS === 'web') { // Web:使用 Blob 走 cos-js-sdk-v5 分支 let body: any = null; if (typeof file === 'string') { // 允许直接传 Base64/DataURL 字符串 body = file; } else if ((file as any)?.blob) { body = (file as any).blob; } else if ((file as any)?.uri) { const resp = await fetch((file as any).uri); body = await resp.blob(); } else if (typeof Blob !== 'undefined' && file instanceof Blob) { body = file; } else { throw new Error('无效的文件:请提供 uri 或 Blob'); } res = await uploadWithRetry({ key, body, contentType: finalOptions.contentType || (file as any)?.type, signal: controller.signal, onProgress: ({ percent }: { percent: number }) => setProgress(percent), } as any); } else { // 原生:直接传本地路径 const srcUri = (file as any)?.uri || (typeof file === 'string' ? file : undefined); if (!srcUri || typeof srcUri !== 'string') { throw new Error('请提供包含 uri 的对象,或传入本地文件路径字符串'); } res = await uploadWithRetry({ key, srcUri, contentType: finalOptions.contentType || (file as any)?.type, signal: controller.signal, onProgress: ({ percent }: { percent: number }) => setProgress(percent), } as any); } const url = (res as any).publicUrl || buildPublicUrl(res.key); return { key: res.key, url }; } finally { setUploading(false); } }, [defaultOptions] ); return useMemo(() => ({ upload, cancel, progress, uploading }), [upload, cancel, progress, uploading]); }