拖放上传需同时监听dragover(必调preventDefault)、drop事件,否则drop不触发;从dataTransfer.files安全取文件需转为真数组并校验长度;上传前须校验大小、类型、生成唯一标识;必须用FormData封装文件及参数,不可手动设headers。

拖放上传不是靠 dragstart 和 drop 两个事件就能跑通的——漏掉 dragover 的默认行为阻止,文件根本进不来。
为什么 drop 事件拿不到文件?
浏览器对 drop 有默认拦截策略:除非显式阻止 dragover 的默认行为,否则 drop 不会触发。这是最常卡住的一步。
- 必须在
dragover回调里调用event.preventDefault() - 仅监听
drop而不处理dragover,控制台不会报错,但事件静默失效 -
dragenter可选,但dragover是强制项
如何从 event.dataTransfer 安全提取文件?
dataTransfer.files 是标准入口,但要注意它返回的是 FileList(类数组),不是数组;且可能为空(比如拖了文件夹或截图到桌面时)。
- 优先用
Array.from(event.dataTransfer.files)转成真数组,方便后续map/filter - 检查
event.dataTransfer.items可兼容「拖入图片到编辑区」等富文本场景,但普通文件上传只需files - 忽略
event.dataTransfer.types里的"Files"判断——它不可靠,Chrome/Firefox 表现不一致
document.addEventListener('drop', (e) => {
e.preventDefault();
const files = Array.from(e.dataTransfer.files);
if (files.length === 0) return;
handleFiles(files); // 自定义上传逻辑
});上传前必须做的三件事
直接调 fetch 或 XMLHttpRequest 发送原始 File 对象看似可行,但实际线上环境大概率失败。
立即学习“Java免费学习笔记(深入)”;
- 校验文件大小:
file.size > 10 * 1024 * 1024(例如限制 10MB) - 过滤非法类型:
!file.type.startsWith('image/') && !file.name.endsWith('.pdf') - 生成唯一标识用于断点续传或去重:
file.lastModified + '-' + file.name比单纯用name更稳妥
为什么 FormData 是必选项?
绕过 FormData 直接把 File 当 body 传,后端几乎无法解析。浏览器要求 multipart/form-data 格式,而 FormData 是唯一能自动生成边界(boundary)和正确头信息的原生方案。
- 不要手动拼接
Content-Type: multipart/form-data—— 浏览器会忽略你设的值,自动覆盖为带 boundary 的真实值 - 字段名必须和后端约定一致,例如后端接收字段叫
upload_file,就要写formData.append('upload_file', file) - 如需额外参数(如 token、目录 ID),统一用
formData.append('token', 'xxx')追加,别混在 URL 查询参数里
async function uploadFile(file) {
const formData = new FormData();
formData.append('upload_file', file);
formData.append('token', getAuthToken());
try {
const res = await fetch('/api/upload', {
method: 'POST',
body: formData // 不要设 headers,让浏览器自动设置
});
return await res.json();
} catch (err) {
console.error('Upload failed:', err);
}
}
真正容易被忽略的是:拖放区域如果嵌套在表单内,drop 后若焦点落在可编辑元素上,可能意外触发表单提交;建议在 drop 处理完后主动 event.stopPropagation() 并确保区域无 form 语义干扰。










