XMLHttpRequest 是唯一能真正中止文件上传的原生方案,其 abort() 方法可立即断开连接、触发 onabort 事件且阻止后续回调执行;fetch 无法真正中断上传,AbortController 仅终止响应读取阶段。

如何用 XMLHttpRequest 中断上传请求
原生 XMLHttpRequest 是唯一能真正中止进行中文件上传的可靠方式。它的 abort() 方法会立即终止底层连接,触发 onabort 事件,并让后续的 onload、onerror 不再执行。
关键点在于:必须在调用 send() 后、请求完成前调用 abort();如果已收到响应或请求超时,abort() 无效。
- 保存
XMLHttpRequest实例到变量(如uploadXhr),供取消按钮调用 - 上传开始后禁用提交按钮,避免重复触发;取消后需重置状态(如清空
uploadXhr) - 不要依赖
onreadystatechange判断是否可取消——只要readyState不是4(DONE),都可安全调用abort()
const uploadXhr = new XMLHttpRequest();
uploadXhr.upload.onprogress = (e) => { /* 更新进度 */ };
uploadXhr.onload = () => { /* 处理成功 */ };
uploadXhr.onerror = () => { /* 处理网络错误 */ };
uploadXhr.send(formData);
<p>// 取消时
document.getElementById('cancelBtn').onclick = () => {
if (uploadXhr.readyState !== 0 && uploadXhr.readyState !== 4) {
uploadXhr.abort();
}
};
fetch 为什么不能直接取消上传
fetch 本身不提供中止上传的接口。即使你传入 AbortController 的 signal,它只能中止「读取响应」阶段,对「发送请求体(尤其是大文件)」几乎无效——浏览器仍会把整个 Body 发完才触发 abort 事件。
这是因为 fetch 的设计模型是“请求发出即不可逆”,其 AbortController 主要用于防止长时间等待响应,而非实时切断上传流。
立即学习“前端免费学习笔记(深入)”;
- 若强行用
AbortController并监听signal.onabort,你会发现上传仍在后台进行,只是后续的response.json()等不会执行 - 某些 Chromium 版本在 DevTools Network 面板里能看到请求状态变为
(cancelled),但服务器很可能已收到完整文件 - 想用
fetch实现真取消?目前没有标准方案,只能退回到XMLHttpRequest
使用 FormData 时取消上传的注意事项
如果你通过 FormData.append('file', input.files[0]) 构建上传数据,取消操作本身不影响 FormData 对象,但要注意:一旦 XMLHttpRequest.send() 被调用,FormData 就被绑定到该请求实例,无法复用。
- 每次新上传必须新建
FormData实例,否则可能混入旧文件或触发奇怪的 MIME 类型错误 - 不要在
uploadXhr.abort()后继续调用send()同一个FormData—— 它已处于不可重用状态,应重新构造 - 如果上传前做了文件切片(如分块上传),取消只需中断当前正在发送的
XMLHttpRequest,不影响其他分片;但需自行清理已上传成功的分片,否则服务器可能残留脏数据
服务端配合:为什么只靠前端取消还不够
前端调用 abort() 只是断开了客户端连接,服务器进程未必立刻感知或停止处理。尤其当后端用了 Node.js 的 busboy、PHP 的 $_FILES 或 Nginx 的临时文件机制,上传数据可能还在写入磁盘或解析中。
真正健壮的取消需要前后端协同:
- 前端在
abort()后,主动发一个带唯一上传 ID 的取消请求(如DELETE /upload/:id) - 后端需维护上传上下文(如内存 Map 或 Redis),收到取消指令后立即释放资源、删除临时文件
- 若使用云存储直传(如 OSS、S3),取消逻辑更复杂——需调用对应 Cancel API 或清理未完成的 multipart upload
没服务端配合的“取消”,只是让用户感觉停了;实际上传可能已完成一半,甚至悄悄成功。



















