轻松上传文件:通过拖拽和粘贴实现
前言
最常见的文件上传方式是:
<input type="file" />
这种方式够用,但交互比较单一,不支持把文件直接拖进页面,也不支持用 Ctrl + V 粘贴图片。
如果想让上传体验更顺手,通常会把这三种方式一起支持:
- 点击选择文件
- 拖拽文件上传
- 粘贴图片上传
核心思路
三种方式本质上都是拿到 File 对象,再交给统一的上传逻辑处理。
input通过event.target.files取文件- 拖拽通过
event.dataTransfer.files取文件 - 粘贴通过
event.clipboardData.items取文件
只要最后都能得到 File,后面的预览、校验、上传都可以共用一套代码。
一个统一示例
<input id="fileInput" type="file" accept="image/*" />
<div id="dropZone" tabindex="0">拖拽文件到这里,或聚焦后直接粘贴图片</div>
<img id="preview" alt="预览图" />
const fileInput = document.getElementById('fileInput')
const dropZone = document.getElementById('dropZone')
const preview = document.getElementById('preview')
function handleFile(file) {
if (!file) return
const reader = new FileReader()
reader.onload = event => {
preview.src = event.target?.result ?? ''
}
reader.readAsDataURL(file)
}
fileInput.addEventListener('change', event => {
const file = event.target.files?.[0]
handleFile(file)
})
dropZone.addEventListener('dragover', event => {
event.preventDefault()
})
dropZone.addEventListener('drop', event => {
event.preventDefault()
const file = event.dataTransfer?.files?.[0]
handleFile(file)
})
dropZone.addEventListener('paste', event => {
const items = event.clipboardData?.items ?? []
for (const item of items) {
if (item.kind === 'file') {
const file = item.getAsFile()
handleFile(file)
break
}
}
})
需要注意的点
拖拽必须阻止默认行为
如果不在 dragover 和 drop 里调用 event.preventDefault(),浏览器通常会直接尝试打开文件,而不是把它交给你的页面处理。
粘贴上传更适合图片
paste 最常见的是处理剪贴板里的图片,例如截图、微信截图、QQ 截图或者系统截图工具复制出来的内容。
文本当然也能粘贴,但文件上传场景里更常用的是图片。
粘贴区域最好可聚焦
像 div 这类元素默认不能获得焦点,所以一般要加上 tabindex="0",不然用户点过去后直接按 Ctrl + V,事件可能不会落在目标元素上。
适合哪些场景
- 富文本编辑器上传图片
- IM 聊天窗口发图
- 工单系统上传截图
- 管理后台快速上传附件
总结
上传文件不一定只能靠 input[type=file]。
如果你想把交互做得更顺手,可以把这三种入口统一起来:
change处理点击选择drop处理拖拽上传paste处理粘贴上传
最后统一交给同一个 handleFile(),代码会更简单,体验也会更好。