From 17a62cd3c2a874dfc55eac8c478b374d6df2802e Mon Sep 17 00:00:00 2001 From: Jerry Yan <792602257@qq.com> Date: Wed, 4 Feb 2026 14:51:23 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E4=B8=8A=E4=BC=A0?= =?UTF-8?q?=E5=8F=96=E6=B6=88=E5=8A=9F=E8=83=BD=EF=BC=8C=E7=A1=AE=E4=BF=9D?= =?UTF-8?q?=20HTTP=20=E8=AF=B7=E6=B1=82=E6=AD=A3=E7=A1=AE=E4=B8=AD?= =?UTF-8?q?=E6=AD=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 XMLHttpRequest 中添加 signal.aborted 检查 - 修复 useSliceUpload 中的 cancelFn 闭包问题 - 确保流式上传和分片上传都能正确取消 --- frontend/src/hooks/useSliceUpload.tsx | 48 ++++++++++++++++++++++----- frontend/src/utils/file.util.ts | 10 ++++++ frontend/src/utils/request.ts | 8 +++++ 3 files changed, 57 insertions(+), 9 deletions(-) diff --git a/frontend/src/hooks/useSliceUpload.tsx b/frontend/src/hooks/useSliceUpload.tsx index 14c8d4c..1fcc62e 100644 --- a/frontend/src/hooks/useSliceUpload.tsx +++ b/frontend/src/hooks/useSliceUpload.tsx @@ -99,8 +99,14 @@ export function useFileSliceUpload( if (!task) { return; } - const { reqId, key } = task; + const { reqId, key, controller } = task; const { loaded, i, j, files, totalSize } = fileInfo; + + // 检查是否已取消 + if (controller.signal.aborted) { + throw new Error("Upload cancelled"); + } + const formData = await buildFormData({ file: files[i], i, @@ -110,6 +116,7 @@ export function useFileSliceUpload( let newTask = { ...task }; await uploadChunk(key, formData, { + signal: controller.signal, onUploadProgress: (e) => { const loadedSize = loaded + e.loaded; const curPercent = Number((loadedSize / totalSize) * 100).toFixed(2); @@ -141,9 +148,10 @@ export function useFileSliceUpload( reqId, isCancel: false, cancelFn: () => { - task.controller.abort(); + // 使用 newTask 的 controller 确保一致性 + newTask.controller.abort(); cancelUpload?.(reqId); - if (task.updateEvent) window.dispatchEvent(new Event(task.updateEvent)); + if (newTask.updateEvent) window.dispatchEvent(new Event(newTask.updateEvent)); }, }; updateTaskList(newTask); @@ -153,8 +161,16 @@ export function useFileSliceUpload( let loaded = 0; for (let i = 0; i < files.length; i++) { + // 检查是否已取消 + if (newTask.controller.signal.aborted) { + throw new Error("Upload cancelled"); + } const { slices } = files[i]; for (let j = 0; j < slices.length; j++) { + // 检查是否已取消 + if (newTask.controller.signal.aborted) { + throw new Error("Upload cancelled"); + } await uploadSlice(newTask, { loaded, i, @@ -219,9 +235,10 @@ export function useFileSliceUpload( reqId, isCancel: false, cancelFn: () => { - task.controller.abort(); + // 使用 newTask 的 controller 确保一致性 + newTask.controller.abort(); cancelUpload?.(reqId); - if (task.updateEvent) window.dispatchEvent(new Event(task.updateEvent)); + if (newTask.updateEvent) window.dispatchEvent(new Event(newTask.updateEvent)); }, }; updateTaskList(newTask); @@ -232,13 +249,26 @@ export function useFileSliceUpload( // 逐个处理文件 for (let i = 0; i < files.length; i++) { + // 检查是否已取消 + if (newTask.controller.signal.aborted) { + throw new Error("Upload cancelled"); + } + const file = files[i]; console.log(`[useSliceUpload] Processing file ${i + 1}/${files.length}: ${file.name}`); const result = await streamSplitAndUpload( file, - (formData, config) => uploadChunk(task.key, formData, config), + (formData, config) => uploadChunk(task.key, formData, { + ...config, + signal: newTask.controller.signal, + }), (currentBytes, totalBytes, uploadedLines) => { + // 检查是否已取消 + if (newTask.controller.signal.aborted) { + return; + } + // 更新进度 const overallBytes = totalProcessedBytes + currentBytes; const curPercent = Number((overallBytes / totalSize) * 100).toFixed(2); @@ -260,9 +290,9 @@ export function useFileSliceUpload( 1024 * 1024, // 1MB chunk size { reqId, - hasArchive: task.hasArchive, - prefix: task.prefix, - signal: task.controller.signal, + hasArchive: newTask.hasArchive, + prefix: newTask.prefix, + signal: newTask.controller.signal, maxConcurrency: 3, } ); diff --git a/frontend/src/utils/file.util.ts b/frontend/src/utils/file.util.ts index dc14d6c..096f15b 100644 --- a/frontend/src/utils/file.util.ts +++ b/frontend/src/utils/file.util.ts @@ -442,6 +442,11 @@ export async function streamSplitAndUpload( * 上传单行内容 */ async function uploadLine(line: string, index: number): Promise { + // 检查是否已取消 + if (signal?.aborted) { + throw new Error("Upload cancelled"); + } + if (!line.trim()) { skippedEmptyCount++; return; @@ -455,6 +460,11 @@ export async function streamSplitAndUpload( const slices = sliceFile(lineFile, DEFAULT_CHUNK_SIZE); const checkSum = await calculateSHA256(slices[0]); + // 检查是否已取消(计算哈希后) + if (signal?.aborted) { + throw new Error("Upload cancelled"); + } + const formData = new FormData(); formData.append("file", slices[0]); formData.append("reqId", reqId.toString()); diff --git a/frontend/src/utils/request.ts b/frontend/src/utils/request.ts index aed6743..23b7c2b 100644 --- a/frontend/src/utils/request.ts +++ b/frontend/src/utils/request.ts @@ -92,6 +92,14 @@ class Request { }); } + // 监听 AbortSignal 来中止请求 + if (config.signal) { + config.signal.addEventListener("abort", () => { + xhr.abort(); + reject(new Error("上传已取消")); + }); + } + // 监听上传进度 xhr.upload.addEventListener("progress", function (event) { if (event.lengthComputable) {