拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

使用 @uppy/drag-drop 打造浏览器拖拽上传区:安装、配置与源码原理

使用 @uppy/drag-drop 打造浏览器拖拽上传区:安装、配置与源码原理
  • 前端
  • UI组件
  • 后端

【免费下载链接】uppy

The next open source file uploader for web browsers :dog:

项目地址:https://gitcode.com/gh_mirrors/up/uppy
点击查看免费下载

导读

@uppy/drag-drop是 Uppy 生态中专用于提供"拖拽放置区域"(Droppable Zone)的 UI 插件:用户把文件拖进该区域即可加入上传队列,也可以点击区域打开系统文件选择对话框。本文以该插件在 当前仓库 中的官方文档与源码实现为骨架,覆盖其安装方式、最小示例、全部可配置选项,并深入DragDrop.tsx与 Uppy Core 工具函数,剖析拖拽事件处理、文件夹递归解析、浏览器能力检测与上传限制联动的底层原理,帮助你既会用、也能看懂它为什么这样工作。

插件定位:Uppy 的 Droppable Zone

@uppy/drag-drop在 Uppy 插件体系中的类型是acquirer(获取者),即在用户上传之前负责"收集"文件的插件。它在页面上渲染一个可拖拽的放置区域("Droppable zone UI for Uppy"),并同时具备两种取文件方式:

  • 拖拽投放:把本地文件或文件夹拖入区域;
  • 点击浏览:点击区域弹出系统文件选择框。

与@uppy/dashboard这类完整界面不同,DragDrop只渲染一个独立的放置区,非常适合嵌入已有页面布局、与自定义上传按钮搭配,或作为简易上传表单的核心交互组件。其组件实现位于 DragDrop.tsx,最终渲染为一个<button>元素,内含隐藏的<input type="file">,无需任何额外 DOM 容器。

安装

通过 npm 安装插件本体及其运行时依赖@uppy/core(peer 依赖):

npm install @uppy/drag-drop

按 package.json 的exports声明,安装后可引入的入口包括:

  • @uppy/drag-drop:插件主入口(lib/index.js);
  • @uppy/drag-drop/css/style.css、@uppy/drag-drop/css/style.min.css:编译后的样式;
  • @uppy/drag-drop/css/style.scss:可直接参与 Sass 构建的源码样式。

若使用 Transloadit Smart CDN 提供的预构建 bundle,Uppy会挂载到全局window.Uppy对象上,此时无需打包器也可直接使用本插件。

基础用法:三行代码接入

官方 README 给出的最小示例(packages/@uppy/drag-drop/README.md):

import Uppy from '@uppy/core' import DragDrop from '@uppy/drag-drop' const uppy = new Uppy() uppy.use(DragDrop, { target: '#upload', })

执行流程是:

  1. new Uppy()创建核心实例;
  2. uppy.use(DragDrop, { target: '#upload' })注册插件,并将放置区挂载到页面中#upload元素内(install()方法中通过this.mount(target, this)完成);
  3. 用户拖入或点选文件后,插件把文件以 descriptor 形式交给uppy.addFiles(),进入 Uppy 的全局状态管理。

若页面已有拖拽 HTML5 能力之外的样式需求,还需引入样式,例如:

import '@uppy/drag-drop/css/style.min.css'

样式文件中定义的.uppy-DragDrop-container等类名详见 style.scss。

可配置选项全解析

插件公开的选项类型DragDropOptions定义在 DragDrop.tsx,与默认值(defaultOptions,见 DragDrop.tsx)合并后生效。汇总如下:

选项类型默认值说明
targetstring \| HTMLElement—插件挂载的 DOM 目标(继承自UIPluginOptions),README 示例中传'#upload'
inputNamestring'files[]'隐藏<input type="file">的name属性,便于配合传统表单提交
widthstring \| number'100%'放置区域宽度,直接作用于容器的style.width
heightstring \| number'100%'放置区域高度,直接作用于容器的style.height
notestring空显示在区域内的辅助提示文字(如"单个文件不超过 5MB")
onDragOver(event: DragEvent) => void—文件拖入区域时回调
onDragLeave(event: DragEvent) => void—拖拽离开区域时回调
onDrop(event: DragEvent) => void—文件投放完成后回调
localeLocaleStrings见下节区域文案的本地化字符串

从源码(DragDrop.tsx)可以看到,三个on*回调分别在handleDragOver、handleDragLeave、handleDrop内部被调用,可用于实现拖拽高亮、统计投放次数等自定义交互逻辑。

与 Uppy 限制规则的联动

值得注意的一点是:@uppy/drag-drop不会重复实现文件校验,而是直接读取 Uppy 核心实例上的restrictions配置,并把它反映到隐藏的 file input 上(DragDrop.tsx):

  • multiple属性由restrictions.maxNumberOfFiles !== 1决定——若限制只能传一个文件,则禁止多选;
  • accept属性由restrictions.allowedFileTypes?.join(', ')生成——在系统文件对话框中预先过滤文件类型。

因此,你只需要在new Uppy({ restrictions: { ... } })中统一配置限制,拖拽区便会自动遵循,无需重复传参。

源码原理:四步事件处理链

整个插件的拖拽逻辑集中在 DragDrop.tsx 的三个事件处理器中:

1.handleDragOver—— 准入检查与视觉反馈

  • 读取event.dataTransfer.types,检查其中是否包含'Files'类型;若拖入的是链接、文本等非文件内容,则拒绝;
  • 检查this.uppy.getState().allowNewUpload,当 Uppy 正在上传且不允许新任务时同样拒绝;
  • 拒绝时设置dropEffect = 'none'并中止;允许时设置dropEffect = 'copy',并把isDraggingOver状态置为true,触发.uppy-DragDrop--isDraggingOver样式(背景变灰、边框变蓝)。

2.handleDragLeave—— 状态复位将isDraggingOver重置为false,恢复默认外观。

3.handleDrop—— 取文件并入库调用 Uppy Core 工具函数getDroppedFiles(event.dataTransfer, { logDropError })异步取回所有文件,非空时记录日志并调用this.addFiles(files)。

4.addFiles—— 构造 descriptor把每个File映射为{ source, name, type, data, meta }结构,其中meta.relativePath取自file.relativePath(用于保留文件夹层级),再统一交给uppy.addFiles()进入 Uppy 状态树;若被Restricter拒绝,错误会通过uppy.log记录而不会中断。

点击路径:render()把整个区域渲染为<button>,onClick触发隐藏 input 的click();onInputChange处理change事件后会把input.value清空(DragDrop.tsx),这样即使用户重复选择同一个文件也能再次触发change。

文件夹拖拽:getDroppedFiles的双策略

放置区支持直接拖入文件夹。其能力来自 Uppy Core 的 getDroppedFiles/index.ts:它优先尝试webkitGetAsEntryApi(利用webkitGetAsEntry()递归遍历目录,把文件夹拍平为文件数组,并为每个文件补上relativePath),若浏览器不支持或解析出错,则回退到fallbackApi仅返回第一层文件。因此:

  • 在 Chrome、Mozilla、Safari 等支持webkitGetAsEntry的浏览器中拖入文件夹,可保留相对路径结构(如docs/Old Prague/airbnb.pdf);
  • 在不支持的浏览器中,仅能取到拖拽项的顶层文件。

getDroppedFiles返回的是 Promise,即便个别目录解析失败(如 Windows 上文件夹名过长),也会通过logDropError记录后继续解析其余文件,保证整体投放流程不中断。

浏览器能力检测

插件在构造时即执行isDragDropSupported()并缓存结果(DragDrop.tsx),用于给容器追加uppy-DragDrop--isDragDropSupported类(决定是否显示虚线边框)。检测逻辑见 isDragDropSupported.ts:

  • 在服务端渲染(无window/document.body)时直接返回false;
  • 依次检查draggable、ondragstart、ondrop属性以及FormData、FileReader是否存在;
  • 移动端浏览器通常不满足这些条件,因而不会显示虚线"可拖放"样式,但点击浏览文件的能力依然可用。

国际化文案

默认文案定义在 locale.ts,仅两条字符串:

key默认文案用途
dropHereOrDrop here or %{browse}区域主文案,%{browse}会被替换为可点击的"browse"链接
browsebrowse打开系统文件对话框的链接文本

通过locale选项可整体替换(%{browse}占位符会被源码中以.uppy-DragDrop-browse高亮的<span>渲染),实现全中文界面只需:

uppy.use(DragDrop, { target: '#upload', locale: { strings: { dropHereOr: '将文件拖到此处,或%{browse}', browse: '浏览', }, }, })

样式定制

放置区的基础视觉由 style.scss 提供,关键类名及作用:

  • .uppy-DragDrop-container:flex 居中布局、圆角、白底与cursor: pointer,聚焦时显示蓝色外发光;
  • .uppy-DragDrop-inner:内边距与居中文案容器;
  • .uppy-DragDrop-arrow:内置的上传箭头 SVG 图标(renderArrowSvg()渲染,aria-hidden);
  • .uppy-DragDrop--isDragDropSupported:灰色虚线边框,标识"可拖放";
  • .uppy-DragDrop--isDraggingOver:拖拽悬停态,背景变浅灰、边框变蓝;
  • .uppy-DragDrop-note:note选项提示文字的颜色与字号。

需要深度定制时,可引入@uppy/drag-drop/css/style.scss参与自己的 Sass 构建,覆盖上述类名即可。

小结

@uppy/drag-drop是一个"小而专"的 Uppy 插件:它只负责提供拖拽/点选入口,把文件统一交还给 Uppy 核心,从而自动获得核心的全局状态管理、restrictions校验与后续上传链路。理解它的选项、事件回调与getDroppedFiles/isDragDropSupported两条底层工具链,可以让你在自定义上传界面时精准掌控拖拽体验,并为移动端、限制规则、文件夹结构保留等边界场景做针对性处理。更多上下文可继续阅读插件入口 index.ts、核心实现 DragDrop.tsx 与官方文档页。

  • 前端
  • UI组件
  • 后端

【免费下载链接】uppy

The next open source file uploader for web browsers :dog:

项目地址:https://gitcode.com/gh_mirrors/up/uppy
点击查看免费下载
上一篇:Qwen3.5-4B-OptiQ-4bit代码生成实战:编程助手的最佳选择
下一篇:华为HCOMM通信同步通知接口

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

返回列表