唯一安全的后缀提取写法是 const ext = file.name.split('.').pop().toLowerCase(),但需校验 ext 是否为空或等于原文件名,并用白名单过滤,且优先按长度降序匹配多点后缀。

file.name.split('.').pop().toLowerCase() 是唯一安全写法
直接用 substring 或正则硬切后缀,在 archive.tar.gz、script.min.js 这类多点文件名上必然出错;lastIndexOf('.') + 1 在无扩展名(如 README)或结尾带点(如 notes.)时会返回空字符串甚至越界。浏览器不保证 File.name 大小写一致,.PDF 和 .pdf 必须归为同一类。
正确提取只有一行:const ext = file.name.split('.').pop().toLowerCase();
但这一行之后必须加校验:
- 如果
ext === ""或ext === file.name(说明没点或点在开头),应 fallback 到通用图标或'unknown' - 必须用白名单过滤:
if (!['jpg', 'png', 'pdf', 'zip'].includes(ext)) ext = 'unknown'; - 匹配图标时优先按长度降序:先查
'tar.gz',再查'gz',否则archive.tar.gz会被误标为 gzip 单文件
为什么不能只信 file.type?
file.type 来自操作系统 MIME 注册表,本地文件常为空(比如 Windows 上双击新建的 .txt 文件),或被用户手动改后缀绕过(把木马改成 report.pdf)。它只能辅助判断,不能替代后缀校验。
真实场景中应组合使用:
- 优先看
file.type.startsWith('image/')判断是否图片预览 -
file.type === ''时,才 fallback 到file.name后缀匹配 - 对 PDF、ZIP、DOCX 等类型,
file.type可能是''或'application/octet-stream',此时后缀就是唯一可靠依据
拖拽进来的文件夹怎么识别?
Safari 拖入文件夹时,e.dataTransfer.files 里会出现一个 size 为 68 字节、name 不含点、type 为空的伪文件,files.length > 0 完全不可靠。
真正有效的识别方式是:
- 遍历
Array.from(e.dataTransfer.files)(必须转真数组) - 逐个检查:
file.size === 68 && !file.name.includes('.') - 同时结合
e.dataTransfer.items判断:item.kind === 'file'才算真实文件 - Android WebView 下
files可能为空但types包含'Files',这种场景建议直接跳过校验,走服务端兜底
图标类名怎么动态加才不泄漏?
用 classList.add() 添加图标类是正确做法,但容易忽略生命周期管理:
- 每次新文件拖入前,先清掉旧的图标类:
el.classList.remove('icon-pdf', 'icon-jpg', 'icon-unknown') - 不要用
className = 'icon-' + ext覆盖——会干掉其他业务 class - 如果支持批量拖入,每个文件对应独立 DOM 元素,确保每个
el只绑自己文件的扩展名类 - 预览用
URL.createObjectURL(file)的,记得在load或error后立刻调URL.revokeObjectURL(url),否则图标 DOM 存活但 blob URL 残留,内存涨得无声无息
后缀提取看着简单,真正卡住人的从来不是“怎么写”,而是“什么时候该停”:拿到 file.name 就急着切后缀,忘了它可能根本不是文件;看到 files.length > 0 就开始读,没意识到 Safari 文件夹正在悄悄塞进一个 68 字节的幽灵。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











