HTML原生不支持上传文件夹,需用webkitdirectory(仅Chromium系)或showDirectoryPicker()(需用户授权)实现;后者可递归遍历并保留路径,配合前端编码与后端安全拼接、先日志后存储、校验与权限控制确保可靠上传。

HTML本身不支持上传文件夹,必须用 webkitdirectory 配合 JavaScript 处理
浏览器原生 默认只允许选单个或多个文件,不能选中整个文件夹。真正能“上传文件夹”的,是 Chrome、Edge、Opera 等基于 Chromium 的浏览器支持的非标准属性 webkitdirectory。它会让 弹出目录选择器,并在 change 事件中返回一个包含所有子文件(不含子目录结构信息)的 FileList。
注意:webkitdirectory 不是 HTML 标准,Firefox 和 Safari 完全不支持;且它不会递归列出子文件夹里的文件——只展平当前选中文件夹下的**直接子文件**(除非手动加 directory 属性并配合 webkitRelativePath 做路径还原,但兼容性更差)。
- 必须显式添加
webkitdirectory和directory属性才能触发文件夹选择模式 -
event.target.files返回的是扁平化的FileList,每个File对象的webkitRelativePath字段可用来还原相对路径(如"docs/report.pdf"),但该字段仅 Chromium 系浏览器提供 - 服务端无法凭空知道“这是哪个文件夹”,必须靠前端把
webkitRelativePath一并传过去
怎么用 JavaScript 获取文件夹内全部文件(含嵌套)并上传
单纯靠 webkitdirectory 只能拿到一级文件。要真正递归上传整个文件夹(含子目录),目前唯一可靠方案是:前端用 showDirectoryPicker()(File System Access API),它支持递归遍历,但仅 Chromium 102+ 且需用户手动授权整个目录。
这个 API 返回一个 FileSystemDirectoryHandle,你可以调用 handle.entries() 递归读取所有文件和子目录,再用 file() 方法逐个读取内容。相比 webkitdirectory,它保留完整目录树,也支持写入(不过上传场景只需读)。
- 必须在安全上下文(
https://或localhost)下运行 - 需要用户主动点击触发(不能 onload 自动调),否则会抛
SecurityError - 每个文件需调用
fileHandle.getFile()获取File实例,再构造FormData上传;建议用name+relativePath组合成唯一键名,比如formData.append("files", file, "src/utils/helper.js") - 别忘了设置
enctype="multipart/form-data",后端才能正确解析多文件
后端怎么记录文件上传日志(以 Node.js + Express 为例)
日志不是前端能完成的事,必须由后端在接收文件时写入。关键点在于:不能等所有文件都存完才记日志,否则中途失败会导致日志与实际存储不一致;推荐「先记日志,再存文件」,并用事务或幂等 key 防重。
使用 multer 接收文件时,可在 fileFilter 或 storage.filename 回调里注入日志逻辑,但更稳妥的是在 req.files 解析完成后、文件流写入磁盘前,统一生成一条日志记录,包含:uploadId(请求唯一标识)、folderName(前端传来的根目录名)、fileCount、每项的 originalName、relativePath、size、sha256(可选校验)。
- 别依赖前端传的
folderName做权限判断——它完全可控,必须结合用户 session 鉴权 - 如果上传量大,避免在主线程同步写日志文件,改用
fs.promises.appendFile或日志库如pino+ 文件目标 - 日志格式建议 JSON 行格式(每行一个 JSON 对象),方便后续用
jq或 ELK 分析 - 错误时也要记日志,比如
"status":"failed","error":"EACCES","file":"config/secrets.env"
容易被忽略的坑:路径拼接、编码、大小限制和权限
前端拼 relativePath 时,Windows 用反斜杠 \,Linux/macOS 用正斜杠 /,但 webkitRelativePath 在所有平台都返回 /;而 showDirectoryPicker() 的 fullPath 则可能含 \。不标准化会导致后端解析失败或路径穿越漏洞。
- 后端收到
relativePath后,必须用path.posix.join()(Node.js)或类似安全拼接方式,禁止直接字符串拼接 - 文件名可能含 Unicode 或控制字符,上传前建议前端用
encodeURIComponent编码relativePath,后端再decodeURIComponent - Nginx 默认限制单次请求体为 1MB,上传文件夹很容易超;需调大
client_max_body_size,同时后端multer也要设limits.fileSize - 用户选了
/etc/shadow这种路径?showDirectoryPicker()会限制只能访问用户手动选中的目录及其子项,但依然建议后端对relativePath做白名单检查(如禁止以../开头)
真正的难点不在“怎么传”,而在“怎么让每一次上传都可追溯、可审计、不丢不乱”。路径处理、日志时机、错误回滚这三块,漏掉任何一环,线上查问题时就只剩猜。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











