node.js分片上传文件

前端 :

<html>

<head>
    <title>分片上传文件</title>
</head>

<body>
    <div class="hei-bg" style="display:block;">
        <div class="user-info" style="display:block;">
            <div class="tc">请上传大文件</div>
            <div class="user-pic picw320">
                <input id="uppic" type="file">
            </div>
            <div id="jd" class="jdb">进度</div>

            <div>
                <input type="button" value="确定" id="userbtn" class="bg-main tc userbtn">
            </div>
        </div>
    </div>
</body>
<script src="/javascripts/jquery.min.js"></script>
<script>
    $(function () {
        $('#userbtn').on('click', async function () {
            var d1 = new Date();
            let file = $("#uppic")[0].files[0], //上传文件主体
                name = file.name, //文件名
                size = file.size, //总大小
                succeed = 0, //当前上传数
                shardSize = 1 * 1024 * 1024, //以1MB为一个分片
                shardCount = Math.ceil(size / shardSize); //总片数

            let attr = [];
            try {
                for (let item = 0; item < shardCount; ++item) {
                    await fn(item); //同步
                    // attr.push(fn(item)); //异步
                }
                await Promise.all(attr); //异步

                $('.jdb').append(' 上传成功');
                var d2 = new Date();
                console.log(parseInt(d2 - d1) / 1000);
            } catch (err) {
                $('.jdb').html(err);
                console.log(err);
            }

            function fn(item) {
                return new Promise((resolve, reject) => {
                    var i = item;
                    var start = i * shardSize, //当前分片开始下标
                        end = Math.min(size, start + shardSize); //结束下标

                    //构造一个表单,FormData是HTML5新增的
                    var form = new FormData();
                    form.append("data", file.slice(start, end)); //slice方法用于切出文件的一部分
                    form.append("name", name); //文件名字
                    form.append("total", shardCount); //总片数
                    form.append("index", i + 1); //当前片数
                    //Ajax提交

                    $.ajax({
                        url: "/sliceUpload",
                        type: "POST",
                        data: form,
                        timeout: 120 * 1000,
                        async: false, //同步
                        processData: false, //很重要,告诉jquery不要对form进行处理
                        contentType: false, //很重要,指定为false才能形成正确的Content-Type
                        success: function (data) {
                            ++succeed;
                            if (typeof (data) == 'string') {
                                try {
                                    data = JSON.parse(data);
                                    console.log(data.msg);
                                } catch (e) {
                                    console.log(data);
                                }
                            }
                            //生成当前进度百分比
                            var jd = `${Math.round(succeed / shardCount * 100)}%`;
                            $('.jdb').html(jd);
                            /*如果是线上,去掉定时,直接callback(),
                            这样写是为方便,本地测试看到进度条变化
                            因为本地做上传测试是秒传,没有时间等待*/
                            setTimeout(resolve, 50);
                        }
                    });
                })
            }
        });
    });
</script>

</html>

服务器端:

async function sliceUpload(req, res) {
    var fs = require('fs');
    var multiparty = require('multiparty'); //文件上传模块
    var form = new multiparty.Form(); //新建表单
    //设置编辑
    form.encoding = 'utf-8';
    //设置文件存储路径
    form.uploadDir = "temp/"; // "Uploads/";
    //设置单文件大小限制
    // form.maxFilesSize = 200 * 1024 * 1024;
    /*form.parse表单解析函数,fields是生成数组用获传过参数,files是bolb文件名称和路径*/
    try {
        let [fields, files] = await new Promise((resolve, reject) => {
            form.parse(req, (err, fields, files) => {
                if (err) reject('test err');
                resolve([fields, files]);
            })
        })

        files = files['data'][0]; //获取bolb文件
        var index = fields['index'][0]; //当前片数
        var total = fields['total'][0]; //总片数
        var name = fields['name'][0]; //文件名称
        var url = 'temp/' + name + index; //临时bolb文件新名字
        fs.renameSync(files.path, url); //修改临时文件名字

        var pathname = 'Uploads/' + name; //上传文件存放位置和名称
        if (index == total) { //当最后一个分片上传成功,进行合并
            // 检查文件是存在,如果存在,重新设置名称
            let NonExist = await new Promise((resolve, reject) => {
                fs.access(pathname, fs.F_OK, (err) => {
                    resolve(err);
                });
            })
            if (!NonExist) {
                var myDate = Date.now();
                pathname = 'Uploads/' + myDate + name;
            }
            logs.info('上传文件:' + pathname);
            /*进行合并文件,先创建可写流,再把所有BOLB文件读出来,
                流入可写流,生成文件
                fs.createWriteStream创建可写流   
                aname是存放所有生成bolb文件路径数组:
                ['Uploads/3G.rar1','Uploads/3G.rar2',...]
            */
            var writeStream = fs.createWriteStream(pathname);
            var aname = [];
            for (let i = 1; i <= total; i++) {
                let url = 'temp/' + name + i;
                let data = await new Promise(function (resolve, reject) {
                    fs.readFile(url, function (error, data) {
                        if (error) reject(error);
                        resolve(data);
                    });
                });
                //把数据写入流里
                writeStream.write(data);
                //删除生成临时bolb文件              
                fs.unlink(url, () => {});
            }
            writeStream.end();
            //返回给客服端,上传成功
            var data = JSON.stringify({
                'code': 0,
                'msg': '上传成功'
            });
            res.send(data); //返回数据    
        } else { //还没有上传文件,请继续上传
            var data = JSON.stringify({
                'code': 1,
                'msg': '继续上传'
            });
            res.send(data); //返回数据    
        }
    } catch (e) {
        logs.info(e);
        res.send(e); //返回数据    
    }
}
原文地址:https://www.cnblogs.com/xbblogs/p/9072808.html