import { storage } from '../storage'; export interface UploadResponse { success: boolean; data?: { url: string; filename: string; size: number; mimeType: string; }; message?: string; } async function getAuthToken(): Promise { return (await storage.getItem(`bestaibest.better-auth.session_token`)) || ''; } /** * 上传文件到服务器 * @param uri 本地文件 URI(可以是 blob: 或 file:// 格式) * @param type 文件类型(image 或 video) */ export async function uploadFile(uri: string, type: 'image' | 'video'): Promise { try { // 如果已经是 https URL,直接返回 if (uri.startsWith('https://') || uri.startsWith('http://')) { return { success: true, data: { url: uri, filename: uri.split('/').pop() || 'file', size: 0, mimeType: type === 'image' ? 'image/jpeg' : 'video/mp4', }, }; } // 先获取 blob 以确定实际的 MIME 类型 const response = await fetch(uri); const blob = await response.blob(); // 从 blob 获取实际的 MIME 类型 let mimeType = blob.type; // 如果 blob 没有 type,则根据参数推断 if (!mimeType || mimeType === 'application/octet-stream') { mimeType = type === 'image' ? 'image/jpeg' : 'video/mp4'; } // 根据 MIME 类型确定文件扩展名 let extension: string; if (mimeType.startsWith('image/')) { const imageExtensions: Record = { 'image/jpeg': 'jpg', 'image/png': 'png', 'image/gif': 'gif', 'image/webp': 'webp', }; extension = imageExtensions[mimeType] || 'jpg'; } else { const videoExtensions: Record = { 'video/mp4': 'mp4', 'video/webm': 'webm', 'video/avi': 'avi', 'video/quicktime': 'mov', }; extension = videoExtensions[mimeType] || 'mp4'; } // 生成文件名 const filename = `${type}_${Date.now()}.${extension}`; // 创建 File 对象 const file = new File([blob], filename, { type: mimeType }); // 创建 FormData const formData = new FormData(); formData.append('file', file); // 获取认证 token const token = await getAuthToken(); // 上传文件(不要手动设置 Content-Type,让浏览器自动添加 boundary) const uploadResponse = await fetch(`https://api.mixvideo.bowong.cc/api/file/upload/s3`, { method: 'POST', headers: token ? { 'Authorization': `Bearer ${token}`, } : {}, body: formData, }); if (!uploadResponse.ok) { const errorData = await uploadResponse.json(); const errorMessage = errorData?.error?.message || errorData?.message || uploadResponse.statusText; throw new Error(`Upload failed: ${errorMessage}`); } const result = await uploadResponse.json(); // 支持多种返回格式: // 1. { status: true, msg: "...", data: "url" } - 实际 S3 上传格式 // 2. { success: true, data: { url, ... } } // 3. { data: { status: true, data: "url" } } if (result.status === true && result.data) { // 格式 1(实际 S3 格式) return { success: true, data: { url: result.data, filename: filename, size: file.size, mimeType: mimeType, }, }; } else if (result.data?.status && result.data?.data) { // 格式 3 return { success: true, data: { url: result.data.data, filename: filename, size: file.size, mimeType: mimeType, }, }; } else if (result.success && result.data) { // 格式 2 return result; } else { throw new Error(result.error?.message || result.msg || result.message || 'Upload failed'); } } catch (error) { console.error('Failed to upload file:', error); return { success: false, message: error instanceof Error ? error.message : '上传失败,请稍后重试', }; } } /** * 批量上传文件 */ export async function uploadFiles( files: Array<{ uri: string; type: 'image' | 'video' }> ): Promise { const uploadPromises = files.map(file => uploadFile(file.uri, file.type)); return Promise.all(uploadPromises); }