fix: 修复上传取消功能,确保 HTTP 请求正确中止

- 在 XMLHttpRequest 中添加 signal.aborted 检查
- 修复 useSliceUpload 中的 cancelFn 闭包问题
- 确保流式上传和分片上传都能正确取消
This commit is contained in:
2026-02-04 14:51:23 +08:00
parent f381d641ab
commit 17a62cd3c2
3 changed files with 57 additions and 9 deletions

View File

@@ -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,
}
);

View File

@@ -442,6 +442,11 @@ export async function streamSplitAndUpload(
* 上传单行内容
*/
async function uploadLine(line: string, index: number): Promise<void> {
// 检查是否已取消
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());

View File

@@ -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) {