原生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/([sS]*)/.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}`);
})

效果:

原文地址:https://www.cnblogs.com/qcblog/p/7142660.html