单个大文件分片上传

后台管理系统中,牵扯到安装包管理。超级大的apk包和ipa包需要进行分片上传,上传前需要进行文件是否重复校验,文件md5计算等操作。

1. 文件分片

因为一般服务器一次可上传的文件大小有限。可以跟接口端讨论确认可传的分片大小。将几个G的文件按照固定的大小分成若干片。

js 复制代码
 // 文件分片
export const SIZE = 10 * 1024 * 1024; // 分片大小 10M
export const createFileChunk = (file, size=SIZE) => {
   const fileChunkList = [];
   let cur = 0;
   while(cur < file.size) {
     fileChunkList.push({
       file: file.slice(cur, cur + size)
     });
     cur += size;
   }
   return fileChunkList;
 };

2. 计算文件的MD5

选择sparkMD5的三方库进行hash值的计算。单个文件使用worker好似不大必要。但是后续如果扩展到文件夹上传,文件夹中如果包含非常多的大小文件,那么worker就会变得非常必要了。

js 复制代码
import WorkerPath from "./1.worker.js";
 // 计算大文件HASH
export const calculate = (fileChunkList) => {
   return new Promise((resolve) => {
     const worker = new WorkerPath();
     worker.postMessage({ fileChunkList });
     worker.onmessage = (e) => {
       const { hash } = e.data;
       if (hash) {
         resolve(hash);
       }
     };
   });
 };

然而解析worker.js需要单独的loader来处理。

首先,需要安装 npm install worker-loader -D

其次需要在webpack中进行loader的配置

js 复制代码
// 在rules中配置
      {
        // 匹配 *.worker.js
        test: /\.worker\.js$/,
        use: {
          loader: "worker-loader",
        },
      }

然后,在worker中进行md5的计算

js 复制代码
// 1.worker.js
importScripts("../static/spark-md5.min.js"); //引入本地的三方库

onmessage = function(event) {
    const { fileChunkList } = event.data;

    const spark = new SparkMD5.ArrayBuffer();
    let count = 0;
    const loadNext = (index) => {
        const reader  = new FileReader();
        reader.readAsArrayBuffer(fileChunkList[index].file);
        reader.onload = (e) => {
            count++;
            spark.append(e.target.result);
            if (count === fileChunkList.length) {
                this.postMessage({
                    hash: spark.end()
                });
                this.close();
            }
            loadNext(count);
        };
    };

    loadNext(0);
};

3. 文件检查

调用接口的check方法检查该文件是否已经上传过。如果上传过则不再上传。如果未上传过则开始上传。

4. 文件上传

根据1,2获取到的分片和md5值,进行上传。所有的分片同时上传,浏览器自动限制6-8个请求。剩余的请求会pending。

js 复制代码
    // 所有的分片同时上传,浏览器自动限制6-8个请求
    const promises = fileChunkList.forEach(({file: chunkFile}, index) =>{
      const passedData = {
         chunk: index + 1,
         file: chunkFile,
         name:formData.filename,
         md5: hash,
         totalChunk: fileChunkList.length,
         position: index * SIZE,
         totalSize: formData.file.size,
         chunked: true,
      };
      // uploadPackFile是自己封装的上传方法
      const promiseTemp = uploadPackFile("/upload", passedData,function() {
        progressRef.current = progressRef.current + 1; // 记录上传进度
      });
    });
    // 等待所有的请求结束
    Promise.all(promises).then(result => {
       // 进行上传结束后的一些操作
    })

文件上传进度的加载状态

文件上传各个阶段都是需要等待的过程,比如"上传中..."。后面的三个点可以动态展示。

代码如下:

html 复制代码
<span>上传中<span className='dot'>...</span></span>

样式如下:

css 复制代码
span.dot {
  display: inline-block;
  height: 1em;
  line-height: 1;
  text-align: left;
  vertical-align: -.25em;
  overflow: hidden;
  text-indent: 0;
}
span.dot::before {
  display: block;
  content: '...\A..\A.';
  white-space: pre-wrap;
  animation: dot 1s infinite step-start both;
}
@keyframes dot {
  33% { transform: translateY(-2em); }
  66% { transform: translateY(-1em); }
}
相关推荐
mCell12 小时前
GSAP ScrollTrigger 详解
前端·javascript·动效
gnip12 小时前
Node.js 子进程:child_process
前端·javascript
excel15 小时前
为什么在 Three.js 中平面能产生“起伏效果”?
前端
excel16 小时前
Node.js 断言与测试框架示例对比
前端
天蓝色的鱼鱼18 小时前
前端开发者的组件设计之痛:为什么我的组件总是难以维护?
前端·react.js
codingandsleeping18 小时前
使用orval自动拉取swagger文档并生成ts接口
前端·javascript
石金龙19 小时前
[译] Composition in CSS
前端·css
白水清风19 小时前
微前端学习记录(qiankun、wujie、micro-app)
前端·javascript·前端工程化
Ticnix19 小时前
函数封装实现Echarts多表渲染/叠加渲染
前端·echarts
用户221520442780019 小时前
new、原型和原型链浅析
前端·javascript