Electron文件分片上传

2021-01-29 07:19:40 浏览数 (1)

获取文件分片

代码语言:javascript复制
let stats = fs.statSync(filepath);//读取文件信息
let chunkSize = 3*1024*1024;//每片分块的大小3M
let size = stats.size;//文件大小
let pieces = Math.ceil(size / chunkSize);//总共的分片数  
function uploadPiece (i){ 
    //计算每块的结束位置
    let startdata = i * chunkSize;
    let enddata = Math.min(size, (i   1) * chunkSize);
    let arr = [];
    //创建一个readStream对象,根据文件起始位置和结束位置读取固定的分片
    let readStream = fs.createReadStream(filepath,
                                         {start: startdata, end: enddata - 1}
                                        );
    //on data读取数据
    readStream.on('data', (data)=>{
        arr.push(data)
    }) 
    //on end在该分片读取完成时触发
    readStream.on('end', ()=>{
        //这里服务端只接受blob对象,需要把原始的数据流转成blob对象,这块为了配合后端才转
        let blob = new Blob(arr)
        //新建formdata数据对象
        var formdata = new FormData();
        let md5Val = md5(Buffer.concat(arr));
        formdata.append("file", blob);
        console.log('blob.size',blob.size)
        formdata.append("md5", md5Val);
        formdata.append("size", size   ''); // 数字30被转换成字符串"30"
        formdata.append("chunk", i   '');//第几个分片,从0开始
        formdata.append("chunks", pieces   '');//分片数
        formdata.append("name", name);//文件名
        post(formdata)//这里是伪代码,实现上传,开发者自己实现
    })
}

获取文件hash值

代码语言:javascript复制
const hashFile = (file) => {
    return new Promise((resolve, reject) => {
        const chunks = Math.ceil(file.size / chunkSize);
        let currentChunk = 0;
        const spark = new SparkMD5.ArrayBuffer();
        const fileReader = new FileReader();

        function loadNext() {
            const start = currentChunk * chunkSize;
            const end = start   chunkSize >= file.size ? file.size : start   chunkSize;
            fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));
        }

        fileReader.onload = e => {
            spark.append(e.target.result); // Append array buffer
            currentChunk  = 1;
            if (currentChunk < chunks) {
                loadNext();
            } else {
                console.log('finished loading');
                const result = spark.end();
                // 如果单纯的使用result 作为hash值的时候, 如果文件内容相同,而名称不同的时候
                // 想保留两个文件无法保留。所以把文件名称加上。
                const sparkMd5 = new SparkMD5();
                sparkMd5.append(result);
                sparkMd5.append(file.name);
                const hexHash = sparkMd5.end();
                resolve(hexHash);
            }
        };
        fileReader.onerror = () => {
            console.warn('文件读取失败!');
        };
        loadNext();
    }).catch(err => {
        console.log(err);
    });
}

分片上传文件

代码语言:javascript复制
const chunkSize = 2 * 1024 * 1024; // 每个chunk的大小,设置为2兆
// 使用Blob.slice方法来对文件进行分割。
// 同时该方法在不同的浏览器使用方式不同。
const blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;

let file = new File();
if (!file) {
    alert('没有获取文件');
    return;
}
const blockCount = Math.ceil(file.size / chunkSize); // 分片总数
const axiosPromiseArray = []; // axiosPromise数组
const hash = await hashFile(file); //文件 hash
// 获取文件hash之后,如果需要做断点续传,可以根据hash值去后台进行校验。
// 看看是否已经上传过该文件,并且是否已经传送完成以及已经上传的切片。
console.log(hash);

for (let i = 0; i < blockCount; i  ) {
    const start = i * chunkSize;
    const end = Math.min(file.size, start   chunkSize);
    // 构建表单
    const form = new FormData();
    form.append('file', blobSlice.call(file, start, end));
    form.append('identifier', file.name);
    form.append('filename', file.name);
    form.append('chunkNumber', i 1);
    form.append('size', file.size);
    form.append('hash', hash);
    // ajax提交 分片,此时 content-type 为 multipart/form-data
    const axiosOptions = {
        onUploadProgress: e => {
            // 处理上传的进度
            console.log(blockCount, i, e, file);
        },
    };
    // 加入到 Promise 数组中
    axiosPromiseArray.push(axios.post('/file/upload', form, axiosOptions));
}
// 所有分片上传后,请求合并分片文件
await axios.all(axiosPromiseArray).then(() => {
    // 合并chunks
    const data = {
        size: file.size,
        name: file.name,
        total: blockCount,
        hash
    };
    axios.post('/file/merge_chunks', data).then(res => {
        console.log('上传成功');
        console.log(res.data, file);
        alert('上传成功');
    }).catch(err => {
        console.log(err);
    });
});

Node上传文件

代码语言:javascript复制
async uploadfile() {
    let that = this;
    let filepath = this.filepath;
    let filename = filepath.split("/").reverse()[0];
    let stats = fs.statSync(filepath);//读取文件信息
    let chunkSize = 3 * 1024 * 1024;//每片分块的大小3M
    let size = stats.size;//文件大小
    let piecesAll = Math.ceil(size / chunkSize);//总共的分片数
    let identifier = parseInt(new Date().getTime() / 1000   "");
    let savefolder = this.userInfo.schoolid   "/live";
    const axiosPromiseArray = []; // axiosPromise数组
    console.info("文件大小:"   size);
    console.info("文件分片:"   piecesAll);
    console.info("identifier", identifier)
    console.info("filename", filename)
    console.info("savefolder", this.userInfo.schoolid   "/live")
    let readPieces = 0;//已读取的文件片数
    for (let i = 0; i < piecesAll; i  ) {
        let startdata = i * chunkSize;
        let enddata = Math.min(size, (i   1) * chunkSize);
        let arr = [];
        //创建一个readStream对象,根据文件起始位置和结束位置读取固定的分片
        let readStream = fs.createReadStream(filepath,
                                             {start: startdata, end: enddata - 1}
                                            );
        //on data读取数据
        readStream.on('data', (data) => {
            arr.push(data)
        })

        //on end在该分片读取完成时触发
        readStream.on('end', () => {
            //这里服务端只接受blob对象,需要把原始的数据流转成blob对象,这块为了配合后端才转
            let blob = new Blob(arr)
            //新建formdata数据对象
            const form = new FormData();
            form.append('file', blob);
            form.append('identifier', identifier);
            form.append('filename', filename);
            form.append('chunkNumber', i   1);
            form.append('savefolder', savefolder);
            //加入到 Promise 数组中
            axiosPromiseArray.push(axios.post(fileuploadUrl   'chunk/up', form, {
                headers:
                {'Content-Type': 'application/x-www-form-urlencoded'}
            }));

            readPieces  = 1;
            that.tipmsg.progress = parseInt((readPieces * 100 / piecesAll)   "")
            if (readPieces === piecesAll) {
                axios.all(axiosPromiseArray).then(() => {
                    // 合并chunks
                    const form = new FormData();
                    form.append('savefolder', savefolder);
                    form.append('identifier', identifier   "");
                    form.append('filename', filename);
                    form.append('thumed', 1   "");
                    form.append('rename', 1   "");
                    axios.post(fileuploadUrl   'chunk/mergevideo', form, {
                        headers: {
                            'Content-Type': 'application/x-www-form-urlencoded'
                        }
                    }
                              ).then(res => {
                        console.log('上传成功');
                        console.log("res.data", res.data);
                        axios.post(apiUrl   "section/update_playback", {
                            sectionid: that.sectionid,
                            mp4code: res.data.obj.mp4code || "h264",
                            playback: res.data.obj.videopath
                        }).then(res3 => {
                            win.close()
                        }).catch(err3 => {
                            win.close()
                        })
                        // win.close()
                    }).catch(err => {
                        console.log(err);
                        win.close()
                    });
                });
            }
        })
    }
}

关键点

记录已经读取的分片数量 当所有分片都已经读取后再调用合并接口

0 人点赞