这是《大胖小课》栏目的专题一《说说文件上传那些事儿》的第5节-《实现文件拖拽和剪贴板上传》
专题已经更新章节:
《大胖 • 小课》- 我是这样理解文件上传原理的
《大胖 • 小课》- 写一个文件上传接口
《大胖 • 小课》- 不用 js 实现文件无刷新上传
《大胖 • 小课》- 玩玩多文件配多进度上传
拖拽上传
html5
的出现,让拖拽上传交互成为可能,现在这样的体验也屡见不鲜,实现上也比较简单。
主要是先定义好一个拖拽区域,从该拖拽区域的事件回调内得到文件的相关信息,前提是需要取消一些事件的默认行为,因为浏览器本身会自动打开或下载文件。
DEMO
说明
- 定义一个允许拖放文件的区域
div.drop-box
- 取消
drop
事件的默认行为e.preventDefault();
,不然浏览器会直接打开文件 - 为拖拽区域绑定事件,鼠标在拖拽区域上
dragover
, 鼠标离开拖拽区域dragleave
, 在拖拽区域上释放文件drop
drop
事件内获得文件信息e.dataTransfer.files
HTML
<div class="drop-box" id="drop-box"> 拖动文件到这里,开始上传 </div> <button type="button" id="btn-submit">上 传</button>
JS
<script> var box = document.getElementById('drop-box'); //禁用浏览器的拖放默认行为 document.addEventListener('drop',function (e) { console.log('document drog'); e.preventDefault(); }); //设置拖拽事件 function openDropEvent() { box.addEventListener("dragover",function (e) { console.log('elemenet dragover'); box.classList.add('over'); e.preventDefault(); }); box.addEventListener("dragleave", function (e) { console.log('elemenet dragleave'); box.classList.remove('over'); e.preventDefault(); }); box.addEventListener("drop", function (e) { e.preventDefault(); //取消浏览器默认拖拽效果 var fileList = e.dataTransfer.files; //获取拖拽中的文件对象 var len=fileList.length;//用来获取文件的长度(其实是获得文件数量) //检测是否是拖拽文件到页面的操作 if (!len) { box.classList.remove('over'); return; } box.classList.add('over'); window.willUploadFileList=fileList; }, false); } openDropEvent(); function submitUpload() { var fileList = window.willUploadFileList||[]; if(!fileList.length){ alert('请选择文件'); return; } var fd = new FormData(); //构造FormData对象 for(var i =0;i<fileList.length;i++){ fd.append('f1', fileList[i]);//支持多文件上传 } var xhr = new XMLHttpRequest(); //创建对象 xhr.open('POST', 'http://localhost:8100/', true); xhr.onreadystatechange = function () { if (xhr.readyState == 4) { var obj = JSON.parse(xhr.responseText); //返回值 if(obj.fileUrl.length){ alert('上传成功'); } } } xhr.send(fd);//发送 } //绑定提交事件 document.getElementById('btn-submit').addEventListener('click',submitUpload); </script>
CODE
https://github.com/Bigerfe/fe-learn-code/tree/master/src/upfiles-demo/demo7
剪贴板上传
掘金的写文编辑器是支持粘贴上传图片的,比如我从磁盘粘贴或者从网页上右键复制图片。
DEMO
说明
- 页面内增加一个可编辑的编辑区域
div.editor-box
,开启contenteditable
- 为
div.editor-box
绑定paste
事件 - 处理
paste
事件,从event.clipboardData || window.clipboardData
获得数据 - 将数据转换为文件
items[i].getAsFile()
- 实现在编辑区域的光标处插入内容
insertNodeToEditor
方法
问题1
测试中发现复制多个文件无效,只有最后一个文件上传,在掘金的编辑器里也同样存在,在坐有知道原因的可以留言说下。
问题2
mac
系统可以支持从磁盘复制文件后上传,windows
系统测试未通过,剪贴板的数据未拿到。
HTML
<div class="editor-box" id="editor-box" contenteditable="true" > 可以直接粘贴图片到这里直接上传 </div>
JS
//光标处插入 dom 节点 function insertNodeToEditor(editor,ele) { //插入dom 节点 var range;//记录光标位置对象 var node = window.getSelection().anchorNode; // 这里判断是做是否有光标判断,因为弹出框默认是没有的 if (node != null) { range = window.getSelection().getRangeAt(0);// 获取光标起始位置 range.insertNode(ele);// 在光标位置插入该对象 } else { editor.append(ele); } } var box = document.getElementById('editor-box'); //绑定paste事件 box.addEventListener('paste',function (event) { var data = (event.clipboardData || window.clipboardData); var items = data.items; var fileList = [];//存储文件数据 if (items && items.length) { // 检索剪切板items for (var i = 0; i < items.length; i++) { console.log(items[i].getAsFile()); fileList.push(items[i].getAsFile()); } } window.willUploadFileList = fileList; event.preventDefault();//阻止默认行为 submitUpload(); }); function submitUpload() { var fileList = window.willUploadFileList||[]; var fd = new FormData(); //构造FormData对象 for(var i =0;i<fileList.length;i++){ fd.append('f1', fileList[i]);//支持多文件上传 } var xhr = new XMLHttpRequest(); //创建对象 xhr.open('POST', 'http://localhost:8100/', true); xhr.onreadystatechange = function () { if (xhr.readyState === 4) { var obj = JSON.parse(xhr.responseText); //返回值 console.log(obj); if(obj.fileUrl.length){ var img = document.createElement('img'); img.src= obj.fileUrl[0]; img.style.width='100px'; insertNodeToEditor(box,img); // alert('上传成功'); } } } xhr.send(fd);//发送 }
CODE
https://github.com/Bigerfe/fe-learn-code/blob/master/src/upfiles-demo/demo8/