electron实现大文件上传

当前遇到得问题:
当electron调用系统dialog选择文件弹窗上传得时候,在主进程传输给渲染进程大得文件流数据时,页面会出现闪退。

解决方案:

  1. 在主进程,把大的文件流分片,
  2. 再分片传输给渲染进程,
  3. 每个分片调用上传文件接口,
  4. 由后台接口分片文件流,
  5. 然后由后台合并成一个完整的文件。

以下是在主进程的关键代码

const _this = this
dialog.showOpenDialog({
    title: '选择发送的文件',
    properties: ['openFile', 'multiSelections']
}).then(result => {
    if (result.filePaths && result.filePaths[0]) {
       
        // 选择文件弹窗选择多个大文件,每个文件都要去分片
        result.filePaths.forEach((filePath,index) => {
            // 每个分片文件流的大小
            const chunkSize = 20*1024*1024
            // fs读取整个文件流
            const stats = fs.statSync(filePath)
            // 文件的总大小
            const size = stats.size
            // 文件分成了多少个分片
            const pieces = Math.ceil(size / chunkSize)
            const fileIndex = index
            // 此处省略代码,把文件路径、文件号、文件大小、分片数传输给渲染进程
            // 以下为关键代码,注意,这里必须使用递归,如果使用for循环,可能会导致分片传输给后台的顺序不对,导致合并文件出错
            AAA(0)
            function AAA(i){
               // 每个分片结束位置
                const enddata = Math.min(size, (i+1)*chunkSize)
                //  arr存储每个分片的文件流
                let arr = []
                 // cuSize 存储当前分片的实际文件大小
                let cuSize = 0
                const chunkIndex = i
                // 获取当前分片从开始到结束的文件流
                const readStream = fs.createReadStream(filePath, {
                    start: i * chunkSize,
                    end:enddata - 1
                })
                // on data读取数据
                readStream.on('data', (data) => {
                    cuSize += data.length
                    arr.push(data)
                })
               //on end在该分片读取完成时触发
                readStream.on('end',()=>{
                    // 此处省略代码,把当前分片的文件流、分片ID号、文件ID号、文件路径、当前分片实际大小传输给渲染进程
                    // 用递归的方式去分片
                    if(i+1 < pieces){
                        AAA(i+1)
                    }
                    
                })
            }
        })
    }
})

在渲染进程的关键代码

const postBigFileUpload = (params,i) => {
    const suffix = params.filePath.split('.').reverse()[0]
    // 这里服务端只接受blob对象,需要把原始的数据流转成blob对象,这块为了配合后端才转
    const file = new Blob(params.fileStream[i])
    let formData = new FormData();
    formData.append('file', file); // 当前分片文件流
    formData.append('suffix', suffix); // 文件的后缀
    formData.append('chunk', i+1); // 第几个分片,从1开始
    formData.append('chunkSize', params.chunkSize); // 当前分片大小
    formData.append('chunks', params.pieces); // 总的分片数
    formData.append('size', params.fileSize);
    axios({
        method: 'post',
        url: '/api/file/bigFileUpload',
        data: formData,
        headers: {
            'Content-Type': 'multipart/form-data',
            'X-Requested-With': 'XMLHttpRequest'
        }
    }).then(res => {
        // 上传成功回调
    })
}
posted @ 2021-09-04 14:31  芙蓉0504  阅读(1967)  评论(0编辑  收藏  举报