"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Upload = void 0; class Upload { controllers = new Map(); constructor() { this.uploadFile = this.uploadFile.bind(this); this.abortUpload = this.abortUpload.bind(this); this.abortAllUploads = this.abortAllUploads.bind(this); this.getUploadStatus = this.getUploadStatus.bind(this); this.getActiveUploads = this.getActiveUploads.bind(this); } async uploadFile(options) { const { file, name, uploadUrl, formData, getPercent, uploadId, method = "POST" } = options; const isPut = method === "PUT"; const id = uploadId || this.generateUploadId(file, uploadUrl); // 如果已有相同ID的上传在进行,先中断它 if (this.controllers.has(id)) { this.abortUpload(id); } // 创建新的 AbortController const controller = new AbortController(); this.controllers.set(id, controller); // 进度监听模式 if (getPercent) { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); let percent = 0; // 监听中止信号 controller.signal.addEventListener('abort', () => { xhr.abort(); reject(new DOMException('Upload aborted', 'AbortError')); }); xhr.upload.addEventListener("progress", (event) => { if (event.lengthComputable) { percent = Math.round((event.loaded / event.total) * 100); getPercent(percent); } }); xhr.onload = () => { this.controllers.delete(id); // 清理控制器 // 构造类似 Response 的对象,支持 headers.get() const headersMap = new Map(); const headersStr = xhr.getAllResponseHeaders(); headersStr.split('\r\n').forEach(line => { const parts = line.split(': '); if (parts.length === 2) { headersMap.set(parts[0].toLowerCase(), parts[1]); } }); const headers = { get: (name) => headersMap.get(name.toLowerCase()) || null, has: (name) => headersMap.has(name.toLowerCase()), forEach: (callback) => { headersMap.forEach(callback); } }; if (xhr.status >= 200 && xhr.status < 300) { if (xhr.status === 204) { resolve({ status: 204, headers }); } else { try { const data = JSON.parse(xhr.responseText); resolve({ ...data, status: xhr.status, headers }); } catch { resolve({ status: xhr.status, raw: xhr.responseText, headers }); } } } else { reject(new Error(`HTTP Error: ${xhr.status}`)); } }; xhr.onerror = () => { this.controllers.delete(id); // 如果不是因为中止导致的错误 if (!controller.signal.aborted) { reject(new Error("Network Error")); } }; xhr.onabort = () => { this.controllers.delete(id); if (controller.signal.aborted) { reject(new DOMException('Upload aborted', 'AbortError')); } }; xhr.open(method, uploadUrl); if (isPut) { // PUT 模式:直接上传文件 if (file instanceof File) { xhr.setRequestHeader("Content-Type", file.type || "application/octet-stream"); } else if (file instanceof Blob) { xhr.setRequestHeader("Content-Type", "application/octet-stream"); } xhr.send(file); } else { // POST / PATCH 模式:构建表单 const formData2 = new FormData(); Object.entries(formData).forEach(([key, value]) => { formData2.append(key, value); }); formData2.append(name || "file", file); xhr.send(formData2); } }); } // 无进度监听模式(直接 fetch) try { let result; if (isPut) { // S3 预签名上传 const headers = {}; if (file instanceof File) { headers["Content-Type"] = file.type || "application/octet-stream"; } else if (file instanceof Blob) { headers["Content-Type"] = "application/octet-stream"; } result = await fetch(uploadUrl, { method: "PUT", headers, body: file, signal: controller.signal, // 添加中止信号 }); } else { // 表单上传 const formData2 = new FormData(); for (const key of Object.keys(formData)) { formData2.append(key, formData[key]); } formData2.append(name || "file", file); result = await fetch(uploadUrl, { method, body: formData2, signal: controller.signal, // 添加中止信号 }); } this.controllers.delete(id); // 成功后清理控制器 return result; } catch (error) { this.controllers.delete(id); // 失败后清理控制器 // 人为中断返回204 使general-business处理成功 if (error instanceof DOMException && error.name === 'AbortError') { throw new DOMException('Upload aborted', 'AbortError'); } throw error; } } // 中断特定上传 abortUpload(uploadId) { const controller = this.controllers.get(uploadId); if (controller) { controller.abort(); this.controllers.delete(uploadId); return true; } return false; } // 中断所有上传 abortAllUploads() { this.controllers.forEach((controller, id) => { controller.abort(); }); this.controllers.clear(); } // 获取上传状态 getUploadStatus(uploadId) { const controller = this.controllers.get(uploadId); if (!controller) return 'not-found'; if (controller.signal.aborted) return 'aborted'; return 'uploading'; } // 生成唯一的上传ID generateUploadId(file, uploadUrl) { const timestamp = Date.now(); const random = Math.random().toString(36).substring(2, 9); const fileInfo = file instanceof File ? `${file.name}-${file.size}` : file instanceof Blob ? `blob-${file.size}` : file; return `${uploadUrl}-${fileInfo}-${timestamp}-${random}`; } // 获取所有进行中的上传任务 getActiveUploads() { return Array.from(this.controllers.keys()); } } exports.Upload = Upload;