原生API实现拖拽上传文件实践
Posted Qcer
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了原生API实现拖拽上传文件实践相关的知识,希望对你有一定的参考价值。
功能:
拖拽上传文件、图片,上传的进度条,能够同时上传多个文件。
完整的demo地址:https://github.com/qcer/FE-Components/tree/master/QDrag
涉及到的API:
1、html5的拖拽事件:dragenter,dragover,drop等
2、XMLHttpRequest Level2
3、FormData
4、(扩展:HTML5的File API)
概述:
1、利用拖拽实践的API将一个普通的div自定义成一个放置目标,这里有一个技巧是放置一个隐藏的input[type=\'file\']的元素,在div上绑定input的点击事件,再点击事件中触发input的click事件,能够在div上任意位置达到type=file选择文件上传的效果。
2、在div的drag事件中获取文件对象,通过FormData对象构造表单序列化的数据,同时动态生成页面元素,通过XMLHttpRequest对象发送数据,在xhr.upload的progress事件中实现上传进度的功能。
3、后端通过nodejs实现一个http服务器,接受数据,进而可以扩展的解析数据。
前端页面代码:
<!DOCTYPE html> <html> <head> <title></title> <style type="text/css"> #qdrag{ height: 300px; width: 100%; background: #eee; border-radius: 5px; padding-top: 20px; } #qdrag-hidden { display: none; position: absolute; z-index: 10; } #qdrag .qdragzone{ width: 100%; height: 50px; background: #ccc; line-height: 50px; border-bottom: 2px solid #fff; border-radius: 2px; overflow: hidden; } #qdrag .qdragzone img{ margin-top: 9px; margin-left: 2%; margin-right: 4%; float: left; } #qdrag .qdragzone span{ display: inline-block; font-weight: bold; font-size: 14px; float: left; width: 20%; color: #339966; font-family: "Times New Roman", Times, serif; /*font-family:Arial,Helvetica,sans-serif;font-size:100%;*/ } #qdrag .qdragzone progress{ border-radius: 6px; height: 12px; width: 250px; color: #5cb85c; background:#fff; } progress::-moz-progress-bar { background: #fff;border-radius: 6px; } progress::-webkit-progress-bar { background: #fff;border-radius: 6px;} progress::-webkit-progress-value {background-color:#5cb85c;border-radius: 6px;} progress::-moz-progress-value { background-color:#5cb85c;border-radius: 6px;} } </style> </head> <body> <div id="qdrag"> <input type="file" id="qdrag-hidden" name="image" value=""></input> <div class="qdragzone"> <img src="./public/upload.png"> <span>Name:test.txt</span> <span>Size:00000 Byte</span> <progress value="0.2" max="1"> </div> </div> <script type="text/javascript"> var qdrag = document.getElementById(\'qdrag\'); var qdrag_hidden = document.getElementById(\'qdrag-hidden\'); qdrag.onclick = function () { qdrag_hidden.click();} qdrag_hidden.onchange = function () { // body... var fileList = this.files; for (var i = 0; i < fileList.length; i++) { sendFileByXHR(\'./upload.html\',fileList[i]); } } function sendFileByXHR(url,fielObj) { // body... var xhr = new XMLHttpRequest(); var newprogress = createTagsEle(fielObj).newprogress; xhr.upload.onprogress = function (event) { // body... console.log(\'xhr-loaded:\'+event.loaded); newprogress.setAttribute(\'value\',event.loaded/event.total); } xhr.onreadystatechange = function () { // body... if (xhr.status === 200 && xhr.readyState === 4) { console.log(xhr.responseText); } } xhr.onabort = function (event) { // body... console.log(\'abort\'); } xhr.onerror = function (event) { // body... console.log(\'error\'); } var data = new FormData(); data.append(fielObj.name,fielObj); xhr.open(\'POST\',url,true); xhr.send(data); } function createTagsEle(fileObj) { // body... //create var fragment = document.createDocumentFragment(); var newdiv = document.createElement(\'div\'); var newimg = document.createElement(\'img\'); var newspanName = document.createElement(\'span\'); var newspanSize = document.createElement(\'span\'); var newprogress = document.createElement(\'progress\'); //set attribute newdiv.setAttribute(\'class\',\'qdragzone\'); newimg.setAttribute(\'src\',\'./public/upload.png\') newspanName.innerHTML = \'Name: \' + fileObj.name; newspanSize.innerHTML = \'Size: \' + fileObj.size+\' Byte\'; newprogress.setAttribute(\'value\',0); newprogress.setAttribute(\'max\',1); //append fragment.appendChild(newdiv); newdiv.appendChild(newimg); newdiv.appendChild(newspanName); newdiv.appendChild(newspanSize); newdiv.appendChild(newprogress); // append to DOM qdrag.appendChild(fragment); return {newprogress}; } qdrag.addEventListener(\'dragover\',function (event) { // body... event.preventDefault(); }); qdrag.addEventListener(\'dragenter\',function (event) { // body... event.preventDefault(); }); qdrag.addEventListener(\'drop\',function (event) { // body... event.preventDefault(); var fileList = Array.from(event.dataTransfer.files); for (var i = 0; i < fileList.length; i++) { sendFileByXHR(\'./upload.html\',fileList[i]); } }); </script> </body> </html>
后端代码:
var http = require(\'http\'); var fs = require(\'fs\') const PORT = 44444; const MIME = { default:\'text/plain\', html:\'text/html\', css:\'text/css\', js:\'text/javascript\', png:\'image/png\', jpg:\'image/jpg\', jpeg:\'image/jpeg\', json:\'application/json\', from:\'multipart/form-data\' } function handleStaticResource(req,res) { // body... var param = req.url.replace(\'/public\',\'\'); var staticResource = fs.readFileSync(\'.\'+param,\'utf8\'); var fileType = req.url.split(\'/\').pop().split(\'.\').pop(); switch(true){ case [\'js\',\'css\'].includes(fileType): res.setHeader(\'Content-Type\',MIME[fileType]); break; case [\'png\',\'jpg\',\'jpeg\'].includes(fileType): staticResource = new Buffer(fs.readFileSync(\'.\'+param,\'base64\'),\'base64\'); console.log(fileType); res.setHeader(\'Content-Type\',MIME[fileType]); break; default : res.setHeader(\'Content-Type\',MIME[\'default\']); console.log(MIME[\'default\']); break; } res.end(staticResource); } var router = function (req,res) { // body... res.render = function (path,options) { // body... var content_html = fs.readFileSync(path,\'utf8\'); res.writeHead(200,{\'Content-Type\':MIME[\'html\']}); res.end(new Buffer(content_html,\'utf8\')); } switch(true){ case /^\\/public\\/([\\s\\S]*)/.test(req.url): handleStaticResource(req,res); break; case req.url === \'/\': // console.log(req.url); res.render(\'./testdrag.html\'); break; case req.url === \'/upload.html\': // console.log(req.headers); var buffers = []; req.on(\'data\',function (chunk) { // body... buffers.push(chunk); }) req.on(\'end\',function () { // body... var data = Buffer.concat(buffers).toString(); res.end("ok");//正确的调用位置 }) // res.end(\'currSize:\' + buffers.length +\' \'+ (new Date()).toGMTString());//错误的调用位置 break; default: // console.log(req.url); res.end(); }; } var server = http.createServer(router); server.listen(PORT,function () { // body... console.log(`the server is linstening on port ${PORT}`); })
效果:
以上是关于原生API实现拖拽上传文件实践的主要内容,如果未能解决你的问题,请参考以下文章