文件上传也能玩出花?Vue3 教你优雅实现“选择文件”和“选择文件夹”🚀

4 阅读3分钟

别再让那个丑丑的原生上传按钮毁了你的页面颜值!

前言:那些年被原生上传按钮支配的恐惧

各位前端小伙伴们,想必大家都遇到过这样的场景:产品经理拿着设计稿,上面是一个精致的、带有圆角渐变和悬停动画的上传按钮,而你打开浏览器,看到了那个灰不溜秋的 <input type="file">……

内心 OS:这玩意儿怎么能变好看???

别慌!今天咱们就用 Vue 3 来搞定这个痛点,而且不仅要能选文件,还要能一键选文件夹!是的,你没看错,就是那种在网盘里才能见到的“上传整个文件夹”功能,咱们前端自己也能实现!

核心思路:偷梁换柱大法 🔥

说白了就一招:把原生 input 藏起来,用自定义按钮去“骗”它干活

就像是你在家里装了个声控灯,表面上你拍手它就亮,实际上后面藏着个物理开关。我们就是那个拍手的人,而 .click() 就是我们的掌声 👏

<!-- 漂亮的自定义按钮 -->
<button @click="triggerUpload('file')">📄 选择 PPT/PDF 文件</button>
<button @click="triggerUpload('folder')">📁 选择文件夹</button>

<!-- 隐藏的原生 input,默默干活 -->
<input ref="fileInputRef" type="file" accept=".ppt,.pptx,.pdf" style="display: none" />
<input ref="folderInputRef" type="file" webkitdirectory style="display: none" />
const triggerUpload = (type) => {
  if (type === 'file') fileInputRef.value.click();  // 这不就点上了嘛!
  else if (type === 'folder') folderInputRef.value.click();
};

黑科技:webkitdirectory 是个什么神仙属性?🧙

这是本次的重头戏!加上这个属性后,用户选中的不再是单个文件,而是整个文件夹。 而且它的返回值是个神奇的东西——event.target.files 会把文件夹里所有子孙文件都平铺成一个数组,不管你的目录嵌套了多少层。

💡 划重点:每个文件对象上有个 webkitRelativePath 属性,它能告诉你这个文件在本地文件夹里的完整相对路径!

比如你选了 我的项目 文件夹,里面有 图片/logo.png,那么 webkitRelativePath 就是 我的项目/图片/logo.png

后端拿到这个路径,就能在服务器上给你原封不动地重建目录结构!是不是很酷?

血泪教训:解决 @change 不触发的千古难题 💀

你有没有遇到过这种情况:

  1. 第一次选了 报告.pptx,正常触发上传
  2. 上传失败了,或者你取消后想重新选一次
  3. 再次点击同一个 报告.pptx
  4. @change 居然不执行了!

原因很简单:浏览器的“鸡贼”逻辑——它发现 input 的 value 没变化(还是同一个文件路径),就认为你没选新文件,所以不触发 change 事件。

解决办法:在每次处理完文件后,手动清空 value

const onFileSelected = (event) => {
  const files = Array.from(event.target.files);
  // ... 处理文件逻辑 ...
  
  // 👇 关键一步,清空 value 防 Bug
  event.target.value = '';
};

这就好比用完遥控器后把电池抠出来再装回去,下次再用绝对灵敏 😂

坑点预警:文件夹上传时 accept 可能“叛变”⚠️

理论上,accept=".ppt,.pptx,.pdf" 能限制用户只能选这三种格式。但在文件夹上传模式下,Chrome 可能会无视这个限制,把文件夹里的 Word 文档、图片、TXT 全给你端上来。

解决方案:别指望浏览器,咱们自己动手过滤!

const onFolderSelected = (event) => {
  const files = Array.from(event.target.files);
  const allowedExtensions = ['.ppt', '.pptx', '.pdf'];
  
  // 手动过滤,只保留我们想要的文件
  const filteredFiles = files.filter(file => 
    allowedExtensions.some(ext => 
      file.name.toLowerCase().endsWith(ext)
    )
  );
  
  // 接着上传 filteredFiles...
};

兼容性提醒 📱

平台文件上传文件夹上传
Chrome/Edge✅ 完美✅ 完美
Firefox✅ 完美✅ 完美
Safari✅ 完美✅ 完美
移动端浏览器✅ 可用❌ 基本不支持

所以,文件夹上传功能建议只在桌面端展示,移动端可以优雅降级,只显示普通的文件上传按钮

最后留个思考题:如果用户选了一个超大文件夹(几百个 G),你觉得前端应该怎么优化体验呢?欢迎评论区讨论~