在日常做后台管理、网盘系统、素材上传功能时,很多前端同学都遇到过同一个头疼问题:小文件上传秒成功,一旦上传几十MB甚至几百MB的大文件,大概率直接报错。要么接口请求超时,要么网络波动导致上传失败,用户只能重新上传,体验极差。
最开始我开发上传功能,也是直接用原生 FormData 整包提交,简单省事。但上线后问题频发,服务器超时、请求中断、重复上传各种BUG层出不穷。后来彻底改用前端分片上传方案后,大文件上传超时、断网重传、卡顿问题基本全部解决。
今天我结合真实项目实战,手把手带大家实现一套 JS 大文件分片上传逻辑,代码可直接复用,同时讲清楚原理、踩坑点和优化思路,适合直接拿来做项目或者面试复盘。
一、为什么大文件上传容易超时?
很多新手不理解,为什么小文件稳得很,大文件就频频翻车。其实原因很简单,常规整包上传是一次性把完整文件二进制数据推给后端。
目前绝大多数服务器、网关都有请求超时时间、单次请求最大体积限制。一般后端接口超时配置也就30秒、60秒,一旦文件过大,传输时间超过阈值,网关直接主动断开连接,页面直接提示超时失败。
除此之外,整包上传还有两个致命短板:网络稍有抖动就全盘重传、无法断点续传,用户体验非常糟糕。而分片上传的核心思路,就是化整为零,把大文件切成无数小块分批上传。
二、分片上传核心原理
我用通俗的话总结一下整套流程,方便大家理解:
1. 前端获取用户上传的 File 文件对象; 2. 自定义固定分片大小,比如每片 5MB; 3. 通过 File.slice() 方法对文件进行二进制切割; 4. 循环遍历分片,逐个携带分片索引、文件唯一标识并发上传; 5. 后端接收所有分片并临时缓存; 6. 全部分片上传完成后,前端发送合并请求,后端合并文件返回最终地址。
这套方案最大的优势就是:单次请求体积小、速度快、不会超时,支持断点续传。
三、前端完整实战代码(可直接运行)
下面给大家写一套原生 JS 分片上传最简可用版本,没有任何框架依赖,原生代码通俗易懂,可直接移植到 Vue、React 项目中。
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>大文件分片上传</title> </head> <body> <input type="file" id="fileInput"> <script> // 分片大小 5MB const SLICE_SIZE = 5 * 1024 * 1024; const fileInput = document.getElementById('fileInput'); fileInput.addEventListener('change', async function (e) { const file = e.target.files[0]; if (!file) return; // 文件总片数 const totalSlice = Math.ceil(file.size / SLICE_SIZE); // 文件唯一标识(简易md5,项目建议用md5加密文件名) const fileKey = file.name + file.size + file.lastModified; // 循环分片上传 for (let i = 0; i < totalSlice; i++) { // 切割分片 const start = i * SLICE_SIZE; const end = Math.min(start + SLICE_SIZE, file.size); const sliceFile = file.slice(start, end); // 组装表单数据 const formData = new FormData(); formData.append('file', sliceFile); formData.append('fileKey', fileKey); formData.append('sliceIndex', i); formData.append('totalSlice', totalSlice); // 上传分片 await uploadSlice(formData); } // 所有分片上传完成,请求合并文件 mergeFile(fileKey, file.name); }); // 上传单个分片 function uploadSlice(data) { return new Promise((resolve) => { fetch('/api/upload/slice', { method: 'POST', body: data }).then(res => res.json()).then(() => { resolve(); }) }) } // 合并分片 function mergeFile(fileKey, fileName) { fetch('/api/upload/merge', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ fileKey, fileName }) }).then(res => res.json()).then(res => { console.log('上传成功', res.url); alert('文件上传完成!'); }) } </script> </body> </html>以上代码是我精简后的核心版本,去掉了冗余逻辑,保留了最核心的分片、遍历、上传、合并流程,本地部署后端接口即可直接跑通。
四、解决超时的关键优化点
很多同学写完分片逻辑还是会偶尔超时,其实是细节没处理好,我分享几个实战优化技巧。
1. 合理设置分片大小
分片不是越小越好,也不是越大越好。太大容易超时,太小请求过多挤占服务器资源。日常项目中,5MB~10MB 是最稳妥的区间,适配绝大多数网关超时配置。
2. 增加请求间隔,避免并发炸接口
上面代码用的串行上传,稳定性最高。如果改成并发,一定要控制并发数量,不然一次性几十条请求涌入,容易被服务器限流,反而导致上传失败。
3. 增加失败重试机制
网络波动很常见,单个分片上传失败不需要整文件重传,可以单独对失败分片重试2-3次,极大提升上传成功率。
五、进阶优化:断点续传(项目必备)
正式项目中,一定要加上断点续传功能。原理很简单:上传前先调用后端接口,查询当前文件已上传的分片索引,前端直接跳过已上传部分,只传未完成的分片,彻底解决中途暂停、刷新页面、断网重传的问题。
配合文件 MD5 唯一标识校验,还能实现秒传功能,重复文件无需再次上传,大幅提升用户体验。
六、总结
大文件上传超时,根本问题不在于网速,而在于传输方式。传统整包上传完全不适合超大文件场景,只要项目涉及视频、压缩包、大素材上传,分片上传几乎是行业标准方案。
这套分片上传逻辑不仅能完美解决接口超时,还能兼容弱网环境、支持断点续传、提升上传稳定性,不管是面试作答还是项目落地,都是非常实用的前端进阶技能。大家可以把本文代码敲一遍,吃透分片逻辑,后续开发大文件上传功能就能直接复用。