关键是在 dragenter 阶段用 dataTransfer.items 判断是否为文件,而非依赖 drop 时的 files;需结合 kind === "file"、size > 0 且 ≠68、后缀与 type 联合校验三重过滤确保有效性。

拖放进来的是不是文件,不能只看 drop 事件触发了没——关键得在 dragenter 或 dragover 阶段就识别清楚,否则用户拖半天才发现不支持,体验直接崩。
怎么用 dataTransfer.items 在拖入瞬间判断是否为真实文件
浏览器在 dragenter 阶段就已提供 e.dataTransfer.items,它比 files 更早可用,且能区分文件、链接、文本等类型。此时文件尚未加载成 File 对象,但每个 DataTransferItem 的 kind 和 type 已可读取。
-
e.dataTransfer.items是只读的DataTransferItemList,支持for循环遍历,但不支持forEach - 每个
item.kind是"file"或"string",这是最可靠的文件标识(比检查files.length更早) -
item.type可用于预判 MIME 类型,比如"image/png"、"application/pdf",但 Safari 对文件夹也返回"file"且type为空,需额外处理 - 不要在
dragover里反复校验,只在dragenter做一次判断并高亮/禁用区域即可
dropArea.addEventListener('dragenter', (e) => {
e.preventDefault();
const items = e.dataTransfer.items;
let hasFile = false;
for (let i = 0; i < items.length; i++) {
if (items[i].kind === 'file') {
hasFile = true;
break;
}
}
if (hasFile) {
dropArea.classList.add('hover');
} else {
dropArea.classList.add('invalid');
}
});
为什么 e.dataTransfer.files.length > 0 不能作为唯一判断依据
这个判断只在 drop 事件里才有效,但此时用户已经完成拖放动作——如果拖进来的是网页链接或纯文本,files.length 确实为 0,但你已经错过了提前反馈的时机。更麻烦的是:Safari 拖入文件夹时,files.length > 0 仍为真,但后续上传会失败(实际是 68 字节伪文件)。
-
e.dataTransfer.files是只读FileList,不能调用filter、map等数组方法 - 必须用
Array.from(e.dataTransfer.files)转成真数组再操作 - 即使
files.length > 0,也要逐个检查file.size > 0并排除 Safari 文件夹(file.size === 68且file.name无扩展名) - 某些 Android WebView 下,
files可能为空但types包含"Files",兼容性差,不建议依赖
drop 事件里怎么安全提取文件并过滤无效项
真正处理上传前,必须同时校验三件事:是不是文件、大小是否超标、类型是否匹配白名单。缺一不可,否则后端可能收不到数据或报错。
立即学习“前端免费学习笔记(深入)”;
- 先调
e.preventDefault(),否则浏览器可能打开文件或跳转链接 - 用
Array.from(e.dataTransfer.files)转数组,再filter掉size === 0或size === 68的项(Safari 文件夹) - 对每个
file,用file.name.match(/\.[^\.]+$/i)提取后缀,并与file.type联合校验(仅靠type不可靠,用户可伪造后缀) - 单个文件超 50MB 建议直接
alert或提示,别塞进FormData再传——XMLHttpRequest可能卡死或被浏览器中断
dropArea.addEventListener('drop', (e) => {
e.preventDefault();
const files = Array.from(e.dataTransfer.files).filter(f =>
f.size > 0 && f.size !== 68
);
if (files.length === 0) return;
files.forEach(file => {
const ext = (file.name.match(/\.[^\.]+$/i) || [''])[0].toLowerCase();
if (!['.jpg', '.png', '.pdf'].includes(ext)) {
console.warn(`不支持的格式: ${ext}`);
return;
}
const formData = new FormData();
formData.append('file', file);
// 后续上传逻辑...
});
});
真正难的不是“怎么拿到文件”,而是“怎么在用户松手前就知道它合不合格”。dragenter 阶段的 items 判断 + drop 阶段的 size/type/name 三重过滤,才是稳住体验的关键。Safari 文件夹和伪造后缀这两点,90% 的线上 bug 都出在这儿。



















