150 lines
4.3 KiB
TypeScript
150 lines
4.3 KiB
TypeScript
import { storage } from '../storage';
|
||
|
||
export interface UploadResponse {
|
||
success: boolean;
|
||
data?: {
|
||
url: string;
|
||
filename: string;
|
||
size: number;
|
||
mimeType: string;
|
||
};
|
||
message?: string;
|
||
}
|
||
|
||
async function getAuthToken(): Promise<string> {
|
||
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<UploadResponse> {
|
||
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<string, string> = {
|
||
'image/jpeg': 'jpg',
|
||
'image/png': 'png',
|
||
'image/gif': 'gif',
|
||
'image/webp': 'webp',
|
||
};
|
||
extension = imageExtensions[mimeType] || 'jpg';
|
||
} else {
|
||
const videoExtensions: Record<string, string> = {
|
||
'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<UploadResponse[]> {
|
||
const uploadPromises = files.map(file => uploadFile(file.uri, file.type));
|
||
return Promise.all(uploadPromises);
|
||
}
|