vue+nodejs实现大文件分片上传后合并

<template>
    <div>
        <div style="margin: 20px">
            <h2>Upload Large Files</h2>
            <br />
            <el-row>
                <el-col :span="4">
                    <input
                        id="uppic"
                        type="file"
                        multiple
                        @change="selectedFiles()"
                        ref="upload"
                    />
                    <label for="uppic">
                        <div id="wrapper">
                            <div id="cell">
                                <span>
                                    <i class="el-icon-folder-opened"></i>
                                    Choose Files
                                </span>
                            </div>
                        </div>
                    </label>
                </el-col>
                <el-col :span="10" :offset="1">
                    <el-button
                        id="userbtn"
                        class="bg-main tc userbtn"
                        type="success"
                        @click="upload"
                    >
                        <i class="el-icon-upload"></i>
                        Upload
                    </el-button>
                </el-col>
            </el-row>
            <el-row v-if="fileList.length != 0">
                <el-table :data="fileList" style="width: 100%">
                    <el-table-column prop="name" label="Name" width="500">
                    </el-table-column>
                    <el-table-column prop="size" label="Size (KB)" width="180">
                    </el-table-column>
                </el-table>
            </el-row>
            <el-row>
                <el-col :span="20">
                    <el-progress
                        :text-inside="true"
                        :stroke-width="26"
                        :percentage="percentage"
                    ></el-progress>
                </el-col>
            </el-row>
        </div>
    </div>
</template>

<script>
import async from "async";
export default {
    data: () => ({
        percentage: 0,
        fileList: [],
    }),
    methods: {
        upload() {
            this.percentage = 0;
            let _this = this;
            // console.log(this.$refs.upload.files);
            for (let k in this.$refs.upload.files) {
                if (k == "0") {
                    let file = this.$refs.upload.files[k], //上传文件主体
                        name = file.name, //文件名
                        size = file.size, //总大小
                        succeed = 0; //当前上传数
                        console.log(file);
                    let shardSize = 2 * 1024 * 1024, //以2MB为一个分片
                        shardCount = Math.ceil(size / shardSize); //总片数
                    // console.log(shardSize);
                    /*生成上传分片文件顺充,通过async.eachLimit()进行同步上传
                    attr里面是[0,1,2,3...,最后一位]    
                */
                    let attr = [];
                    for (let i = 0; i < shardCount; ++i) {
                        attr.push(i);
                    }
                    console.log(attr);
                    async.eachLimit(
                        attr, //原始数据数组
                        1, //每次并行处理的数据量
                        async function (item, callback) {//数据进行的处理 item 为原始数据数组的元素
                            
                            // console.log(item);
                            let i = item;
                            let start = i * shardSize, //当前分片开始下标
                                end = Math.min(size, start + shardSize); //结束下标
                            //构造一个表单,FormData是HTML5新增的
                            let form = new FormData();
                            form.append("data", file.slice(start, end)); //slice方法用于切出文件的一部分
                            form.append("name", name); //文件名字
                            form.append("total", shardCount); //总片数
                            form.append("index", i + 1); //当前片数'

                            await this.http.post("接口地址",form,{
                                timeout: 120*1000
                            })
                            .then(res=>{
                                ++succeed;

                                    /*返回code为0是成功上传,1是请继续上传*/
                                    if(res.data.code==0){
                                        console.log(res.data.mssg);
                                         _this.percentage=0
                                          _this.fileList.shift()
                                         _this.$message({
                                           type:'success',
                                           message:'upload finish'+file.name,
                                           showClose:true

                                       })
                                    }else if(res.data.code==1){

                                        console.log(res.data.msg);
                                    }
                                    //生成当前进度百分比
                                    _this.percentage=Math.round(succeed/shardCount*100);

                                    callback()
                            })
                        },
                        function (err) {
                            // console.log('上传成功');
                        }
                    );
                }
            }
        },
        selectedFiles() {
            // console.log('selected',this.$refs.upload.files)
            this.fileList = [...this.$refs.upload.files];
        },
    },
    watch: {},
};
</script>

<style scoped>
#uppic {
    width: 0.1px;
    height: 0.1px;
    opacity: 0;
    overflow: hidden;
    position: absolute;
    z-index: -1;
}

.el-row {
    margin-bottom: 20px;
}
label {
    color: aquamarine;
    background-color: #796e02e3;
    height: 40px;
    display: block;
    text-align: center;
}
#wrapper {
    display: table;
    height: 40px;
    margin: 0 auto;
    color: rgb(255, 255, 255);
}
#cell {
    display: table-cell;
    vertical-align: middle;
}
</style>

node端代码:

  • 引入并调用user方法:
const upload= require("../middleware/upload.js");
router.post("/fpsc", async (req, res) => {
  	upload(req,res)
})
  • upload方法
function upload(req, res, config){

    let fs = require('fs');
    let async = require('async');//异步模块
    const uuid = require('node-uuid');

    let formidable = require('formidable')
    let form = new formidable.IncomingForm();


    //设置编辑
    form.encoding = 'utf-8';

    let dirPath = __dirname + "/../uploadFiles/tep/";

    //设置文件存储路径
    form.uploadDir = dirPath;
    //设置单文件大小限制
    // form.maxFilesSize = 200 * 1024 * 1024;
    /*form.parse表单解析函数,fields是生成数组用获传过参数,files是bolb文件名称和路径*/
    form.parse(req, function (err, fields, files) {
        files = files['data'];//获取bolb文件
        let index = fields['index'];//当前片数
        let total = fields['total'];//总片数
        let name = fields['name'];//文件名称
        let url = dirPath + '/' + name.split('.')[0] + '_' + index + '.' + name.split('.')[1];//临时bolb文件新名字
        fs.renameSync(files.path, url);//修改临时文件名字

        try {
            if (index == total) {//当最后一个分片上传成功,进行合并
                /*
                    检查文件是存在,如果存在,重新设置名称
                */
                let uid = uuid.v4()

                fs.mkdirSync(__dirname + "/../uploadFiles/" + uid)
                let pathname = __dirname + "/../uploadFiles/" + uid + '/' + name;//上传文件存放位置和名称
                fs.access(pathname, fs.F_OK, (err) => {
                    if (!err) {
                        let myDate = Date.now();
                        pathname = dirPath + '/' + myDate + name;
                        console.log(pathname);

                    }
                });
                //这里定时,是做异步串行,等上执行完后,再执行下面
                setTimeout(function () {
                    /*进行合并文件,先创建可写流,再把所有BOLB文件读出来,
                        流入可写流,生成文件
                        fs.createWriteStream创建可写流   
                        aname是存放所有生成bolb文件路径数组:
                        ['Uploads/img/3G.rar1','Uploads/img/3G.rar2',...]
                    */
                    let writeStream = fs.createWriteStream(pathname);
                    // console.log(writeStream);
                    let aname = [];
                    for (let i = 1; i <= total; i++) {
                        let url = dirPath + '/' + name.split('.')[0] + '_' + i + '.' + name.split('.')[1];
                        aname.push(url);
                    }

                    //async.eachLimit进行同步处理
                    async.eachLimit(aname, 1, function (item, callback) {
                        //item 当前路径, callback为回调函数
                        fs.readFile(item, function (err, data) {
                            if (err) throw err;
                            //把数据写入流里
                            console.log(data);
                            writeStream.write(data);
                            //删除生成临时bolb文件              
                            fs.unlink(item, function () { console.log('删除成功'); })
                            callback();
                        });
                    }, function (err) {
                        if (err) throw err;
                        //后面文件写完,关闭可写流文件,文件已经成生完成
                        writeStream.end();



                        //返回给客服端,上传成功
                        let data = JSON.stringify({
                            'code': 0, "data": {
                                "source_store_id": uid,
                                "file_name": name
                            }
                        });
                        res.writeHead(200, { 'Content-Type': 'text/html;charset=utf-8' });
                        res.end(data);//返回数据    
                    });
                }, 50);

            } else {//还没有上传文件,请继续上传
                let data = JSON.stringify({ 'code': 1, 'msg': '继续上传' });
                res.writeHead(200, { 'Content-Type': 'text/html;charset=utf-8' });
                res.end(data);//返回数据    
            }
        } catch (err) {
            console.log(err)
        }

    });
}


module.exports = upload;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值