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

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

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

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

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

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

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

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

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

html 复制代码
<!-- 漂亮的自定义按钮 -->
<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" />
javascript 复制代码
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 居然不执行了!

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

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

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

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

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

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

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

javascript 复制代码
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),你觉得前端应该怎么优化体验呢?欢迎评论区讨论~

相关推荐
锋行天下19 小时前
半秒开!还有谁!!!
前端·vue.js·架构
JING小白21 小时前
Day 1 重学Vue:响应式系统的“底层逻辑”变更,Vue2旧时代的终结与Vue3新时代的开启
前端·vue.js
OpenTiny社区1 天前
从零开发 AI 聊天页要两周?试试这款 Vue3 垂直对话组件库 TinyRobot,直接开箱即用
前端·vue.js·github
Cobyte1 天前
22.Vue Vapor 组件 props 的实现
前端·javascript·vue.js
白雾茫茫丶1 天前
探索 Nuxt.js 全栈能力:用 Better-Auth 打造类型安全的 RBAC 权限系统
前端·vue.js·nuxt.js
3630458411 天前
Signal 带来的架构问题思考
前端·vue.js
古夕2 天前
第三方 SSO 接入实践:redirect_uri 编码、回调一致性与跨项目联调
前端·vue.js
Ruihong2 天前
Vue withDefaults 转 React:VuReact 怎么处理?
vue.js·react.js·面试
稀土熊猫君2 天前
一个人能做出什么开源项目?
vue.js·后端·开源