别再让那个丑丑的原生上传按钮毁了你的页面颜值!
前言:那些年被原生上传按钮支配的恐惧
各位前端小伙伴们,想必大家都遇到过这样的场景:产品经理拿着设计稿,上面是一个精致的、带有圆角渐变和悬停动画的上传按钮,而你打开浏览器,看到了那个灰不溜秋的 <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 不触发的千古难题 💀
你有没有遇到过这种情况:
- 第一次选了
报告.pptx,正常触发上传 - 上传失败了,或者你取消后想重新选一次
- 再次点击同一个
报告.pptx - @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),你觉得前端应该怎么优化体验呢?欢迎评论区讨论~