HTML表单文件切片上传实战:Input type=file实现

作者:技术拾光

时间:2026-07-27

浏览:132

切片上传需开启multiple支持多文件,accept仅初筛;文件大小限制依赖JavaScript。进度条用语义化progress标签。推荐Blob.slice()与fetch()直传,避免FileReader大文件内存问题。

文章目录

先说几个核心判断:multiple 属性是“不必须,但别省”

很多朋友写文件上传功能时,容易在 input[type="file"] 的 multiple 属性上犹豫:到底要不要加?加了才支持切片上传吗?

先说结论:不是必须的,但强烈建议打开。

不加 multiple,用户每次只能选一个文件,你后端再怎么写切片逻辑,每回都只能处理单个文件——这直接打断了批量上传的流程。而加上 multiple 后,用户一次能选多个文件,每个文件再独立切片,逻辑上更顺畅,用户操作也更自然。

一点细节:一旦开启 multiple,input.files 返回的是 FileList(类数组对象),而不是单个 File 对象。写遍历前最好确认一下长度:

const files = input.files;
for (let i = 0; i < files.length; i++) {
  const file = files[i]; // 每个 file 都是 File 实例,可以 .slice()
}
  • 不加 multiple 也能切片,但那只是单文件场景,扩展性很差
  • File 对象的 slice() 方法兼容 IE10+ 和所有现代浏览器,不用额外补丁
  • 千万别用 input.value 来判断是否选中文件——它只返回文件名,且路径会被浏览器屏蔽。正确的做法是检查 input.files.length > 0

文件选择范围与大小限制:HTML 只能做“初筛”,真拦截得靠 JS

HTML 层面并不能真正执行“切片”操作,但它能帮我们提前过滤掉明显不合规的文件,减少无效的 JS 处理和后续请求。关键靠 accept 属性和 JS 联合把关。

accept 控制文件类型,比如 accept="image/*,.pdf",但它只是提示性的,用户完全可以通过“全部文件”选项绕过。真正要拦截,还是得在 JS 里做:

input.addEventListener('change', (e) => {
  const file = e.target.files[0];
  if (file && file.size > 500 * 1024 * 1024) { // 500MB
    alert('文件超过 500MB,请压缩或分卷');
    e.target.value = ''; // 清空 input,否则下次 change 不触发
  }
});

几个要点:

  • accept 的值尽量不要写死 MIME 类型,比如 application/octet-stream,部分浏览器不识别。优先用扩展名或通用类型(如 image/*)
  • 移动端 Safari 对 accept 支持较弱,依赖 JS 校验更可靠
  • 文件大小限制只能在 JS 里检查,HTML 的 max 属性对 type="file" 无效

切片时的进度条和暂停按钮:必须用原生语义化元素

很多实现都直接拿 div 模拟进度条,简单是简单,但对辅助技术不友好。正确的做法是用 progress 标签和 formnovalidate 按钮。

是语义化的进度指示器,屏幕阅读器可以识别。暂停或取消操作应该绑定到 ,同时注意同步更新 disabled 状态:



几点建议:

  • 不要用
    替代 ,它对辅助技术不可见
  • 暂停功能实际由 JS 通过 abort() 或中断当前 chunk 请求来控制,HTML 层只提供交互入口
  • 切片上传过程中,input 应当保持 disabled 状态,防止用户重复点击触发新上传
  • FileReader 读取大文件为什么容易卡住?怎么绕开?

    这个问题其实是个经典的坑。FileReader.readAsArrayBuffer() 是同步读取整个文件块到内存的——假如你切了 10MB 一片,1GB 的文件就需要一次性加载 10MB 的 ArrayBuffer。浏览器直接可能就卡死了,甚至触发内存警告。

    正确的做法是用 Blob.slice() + fetch() 直传,绕过 FileReader:

    const chunk = file.slice(start, end); // 返回新 Blob,不读入内存
    fetch('/upload', {
      method: 'POST',
      body: chunk,
      headers: {
        'Content-Range': `bytes ${start}-${end-1}/${file.size}`,
        'X-File-Name': file.name,
        'X-Chunk-Index': i
      }
    });
    

    关键逻辑:

    • 只要服务端支持接收 raw Blob,就完全不需要 FileReader——这是现代切片上传的推荐路径
    • 如果必须用 FileReader(比如要前端计算 MD5),务必限制单次读取大小(≤8MB),并用 setTimeout 分批读取,避免阻塞主线程
    • FileReader 的 onload 回调里,this.result 是 ArrayBuffer,别直接转成字符串处理,尤其对二进制文件

    说到底,真正的复杂点不在标签本身

    切片上传真正的挑战并不在 HTML 标签上,而在于如何让表单控件的状态(选中、禁用、反馈)与 JS 的异步切片生命周期严格对齐。漏掉一次 input.disabled = true,或者忘记重置 progress.value,用户就会看到“上传中”却点不了任何按钮——这才是真正容易踩的坑。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›