feat: 新增 S3 文件上传组件,支持文件大小和数量限制,优化上传流程
This commit is contained in:
78
src/utils/aws/s3.ts
Normal file
78
src/utils/aws/s3.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import { toRefs } from 'vue';
|
||||
import { client, safeClient } from '@/service/api';
|
||||
|
||||
interface UploadOptions {
|
||||
fetchOptions: UploadFetchOptions;
|
||||
onProgress?: (progress: number) => void;
|
||||
signal?: AbortSignal;
|
||||
}
|
||||
|
||||
export type UploadFetchOptions = CommonType.TreatyBody<typeof client.api.file_storage.upload_url.post>;
|
||||
|
||||
export async function uploadToS3(file: File, options: UploadOptions) {
|
||||
const { onProgress, signal, fetchOptions } = options;
|
||||
|
||||
// 1. 获取预签名 URL
|
||||
const { data, error } = await safeClient(
|
||||
client.api.file_storage.upload_url.post({
|
||||
...fetchOptions
|
||||
})
|
||||
);
|
||||
|
||||
if (error.value || !data.value) {
|
||||
throw new Error('获取上传 URL 失败');
|
||||
}
|
||||
const { fileId, uploadUrl, method, headers } = toRefs(data.value);
|
||||
|
||||
// 2. 上传文件到 S3
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
const xhr = new XMLHttpRequest();
|
||||
|
||||
// 监听上传进度
|
||||
xhr.upload.addEventListener('progress', (e: ProgressEvent) => {
|
||||
if (e.lengthComputable && onProgress) {
|
||||
const progress = Math.round((e.loaded / e.total) * 100);
|
||||
onProgress(progress);
|
||||
}
|
||||
});
|
||||
|
||||
// 上传成功
|
||||
xhr.addEventListener('load', () => {
|
||||
if (xhr.status >= 200 && xhr.status < 300) {
|
||||
resolve(fileId.value);
|
||||
} else {
|
||||
reject(new Error(`上传失败: ${xhr.status} ${xhr.statusText}`));
|
||||
}
|
||||
});
|
||||
|
||||
// 网络错误
|
||||
xhr.addEventListener('error', () => {
|
||||
reject(new Error('网络错误,上传失败'));
|
||||
});
|
||||
|
||||
// 上传取消
|
||||
xhr.addEventListener('abort', () => {
|
||||
reject(new Error('上传已取消'));
|
||||
});
|
||||
|
||||
// 超时
|
||||
xhr.addEventListener('timeout', () => {
|
||||
reject(new Error('上传超时'));
|
||||
});
|
||||
|
||||
// 支持外部取消
|
||||
if (signal) {
|
||||
signal.addEventListener('abort', () => {
|
||||
xhr.abort();
|
||||
});
|
||||
}
|
||||
|
||||
// 开始上传
|
||||
xhr.open(method.value, uploadUrl.value, true);
|
||||
for (const [key, value] of Object.entries(headers.value)) {
|
||||
xhr.setRequestHeader(key, value);
|
||||
}
|
||||
xhr.timeout = 300000; // 5分钟超时
|
||||
xhr.send(file);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user