HTML原生不支持文件夹上传,必须用webkitdirectory;该非标准属性仅Chrome/Edge/Safari16.4+支持,需配合directory和multiple使用,返回扁平化FileList,路径信息依赖file.webkitRelativePath。

HTML原生不支持文件夹上传,必须用 webkitdirectory
浏览器标准 HTML 表单(<input type="file">)默认只允许选中文件,不能选中文件夹。要上传整个文件夹,必须显式启用 WebKit/Blink 的私有属性 webkitdirectory,且仅在 Chrome、Edge、新版 Safari(16.4+)中可用。Firefox 完全不支持,且该属性未进入标准,未来可能变动。
正确写法是:
<input type="file" webkitdirectory directory multiple />
注意三点:webkitdirectory 必须和 directory 同时存在;multiple 属性不可少(否则只触发一次选择,无法递归读取子项);它会把文件夹内所有文件(含子目录下)以扁平化 FileList 形式返回,但不保留目录结构信息。
JavaScript 读取文件并计算 MD5 需用 FileReader + spark-md5
浏览器无法直接调用系统命令算 MD5,也不能同步读取文件内容。必须用 FileReader 异步读取二进制数据,再交由 JS MD5 库处理。原生 crypto.subtle.digest() 支持 SHA-256,但不支持 MD5(因不安全,已被 Web Crypto API 移除)。所以得依赖第三方库。
立即学习“前端免费学习笔记(深入)”;
推荐使用轻量、流式支持好的 spark-md5(比 js-md5 更适合大文件):
- 通过
npm install spark-md5或 CDN 引入spark-md5.min.js - 对每个
File实例,用SparkMD5.ArrayBuffer分块读取,避免内存爆炸 - 不要一次性
file.arrayBuffer()—— 大文件会卡死 UI 或触发 OOM
示例关键逻辑:
const spark = new SparkMD5.ArrayBuffer();<br>const reader = new FileReader();<br>reader.onload = e => {<br> spark.append(e.target.result);<br> if (reader.readyState === FileReader.DONE) {<br> const md5 = spark.end();<br> console.log(file.name, md5);<br> }<br>};<br>reader.readAsArrayBuffer(file);
文件夹上传后无法直接获取完整路径,需靠 file.webkitRelativePath
webkitdirectory 模式下,每个 File 对象会带一个非标准属性 webkitRelativePath,值为该文件相对于所选文件夹的路径(如 "docs/report.pdf" 或 "img/logo.png")。这是唯一能还原原始目录结构的方式。
但要注意:webkitRelativePath 在 Firefox 中为 "",Safari 旧版本可能为空字符串或不一致;服务端若需按路径组织文件,必须把这个字段和文件内容一起传过去,不能靠服务端重建路径。
常见错误:直接用 file.name 当作完整路径 —— 它只是文件名,不含任何父级目录信息。
MD5 计算耗时明显,必须加 UI 反馈与并发控制
一个 100MB 文件在普通笔记本上算 MD5 可能需 3–5 秒,若用户选了几十个文件,全部并发计算会阻塞主线程、拖慢页面响应,甚至触发浏览器警告。
实操建议:
- 用
Promise.allSettled()控制并发数(例如最多 3 个同时算),避免全量Promise.all() - 每个文件计算前显示“正在校验:xxx”,完成后更新为“✅ xxx(a1b2c3…)”
- 提供取消按钮:保存每个
FileReader实例引用,调用reader.abort() - 对超大文件(>500MB)建议跳过 MD5 或改用分块哈希摘要(如前 1MB + 后 1MB + 总大小)
没有后台线程(Web Worker)参与的话,纯前端算 MD5 本质是妥协方案——它可靠但慢;真要高性能,应让服务端接收文件后异步计算并回调结果。



















