功能:
拖拽上传文件、图片,上传的进度条,能够同时上传多个文件。
完整的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.txtspan>
<span>Size:00000 Bytespan>
<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}`); })
效果: