如何用Javascript实现拖放功能与文件上传?

12次阅读

必须阻止dragover默认行为才能触发drop事件,再读取e.dataTransfer.files获取文件列表,最后用FormData发送请求;accept属性无效,校验须在drop回调中手动完成。

如何用Javascript实现拖放功能与文件上传?

直接用 dragover + drop 事件就能实现基础拖放,但真正能上传文件的完整链路,必须处理好三件事:阻止默认行为、读取 DataTransfer 对象、用 FormData 发送请求。漏掉任意一步,拖进去就啥也不发生。

为什么拖进页面没反应?preventDefault() 必须加在 dragover

浏览器dragover 事件有默认拦截策略——不阻止它,就根本触发不了 drop。这不是可选项,是硬性前提。

  • dragenterdragover 都要监听,但只有 dragover 必须调用 e.preventDefault()
  • 只阻止 dragenter 没用;只阻止 drop 更没用(它压根不会触发)
  • 如果目标区域是 contenteditable 或表单控件,可能还要额外处理 dragleave 防止样式错乱
const dropZone = document.getElementById('drop-area'); dropZone.addEventListener('dragover', (e) => {   e.preventDefault(); // 关键!没有这行,下面的 drop 永远不会来 }); dropZone.addEventListener('drop', (e) => {   e.preventDefault();   const files = e.dataTransfer.files; // 这里才能拿到文件列表 });

e.dataTransfer.filese.dataTransfer.items 怎么选?

两者都能取文件,但行为差异明显:filesFileList(只含本地文件),itemsDataTransferItemList(支持文件、文本、URL 等多种类型,且可递归读取目录)。

  • 纯文件上传场景,优先用 e.dataTransfer.files —— 简单、兼容性好、无权限问题
  • 需要支持拖入整个文件夹?必须用 items + webkitGetAsEntry()chrome/safari),且需用户授权(Filesystem API 已废弃,仅剩此路径)
  • itemsfirefox 中对本地文件返回空 getAsFile(),所以不能只依赖它
dropZone.addEventListener('drop', (e) => {   e.preventDefault();   const files = e.dataTransfer.files; // ✅ 推荐:稳定获取 FileList   // const items = e.dataTransfer.items; // ⚠️ 要配合 entry 处理,且兼容性差   Array.from(files).forEach(file => {     console.log(file.name, file.size, file.type);   }); });

FormData 上传时,fetchXMLHttpRequest 有啥区别

本质没区别,但 fetch 默认不发 Content-Type 头(由浏览器自动设为带 boundary 的 multipart),而 XMLHttpRequest 若手动设了 Content-Type 反而会破坏边界符,导致后端解析失败。

立即学习Java免费学习笔记(深入)”;

  • fetch:完全不要设 headers: {'Content-Type': ...},让它自动生成
  • XMLHttpRequest:调用 xhr.send(formdata) 即可,千万别 xhr.setRequestHeader
  • 如需上传进度,XMLHttpRequest.upload.onprogress 更直接;fetch 需配合 Readablestream + TransformStream,复杂度高得多
const formData = new FormData(); Array.from(files).forEach(file => formData.append('files', file)); 

// ✅ 正确:fetch 不设 Content-Type fetch('/upload', { method: 'POST', body: formData });

// ✅ 正确:XHR 不设 header const xhr = new XMLHttpRequest(); xhr.upload.onprogress = e => console.log(${(e.loaded / e.total * 100).toFixed(0)}%); xhr.open('POST', '/upload'); xhr.send(formData);

最易被忽略的是:拖放本身不校验文件类型或大小,accept 属性在拖放中完全无效,所有校验必须在 drop 回调里手动做。前端拦不住恶意文件,但至少别让超大文件卡死浏览器再报错。

text=ZqhQzanResources