单个大文件分片上传

后台管理系统中,牵扯到安装包管理。超级大的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); }
}
相关推荐
YBN娜7 分钟前
Vue实现登录功能
前端·javascript·vue.js
阳光开朗大男孩 = ̄ω ̄=7 分钟前
CSS——选择器、PxCook软件、盒子模型
前端·javascript·css
minDuck11 分钟前
ruoyi-vue集成tianai-captcha验证码
java·前端·vue.js
小政爱学习!32 分钟前
封装axios、环境变量、api解耦、解决跨域、全局组件注入
开发语言·前端·javascript
魏大帅。37 分钟前
Axios 的 responseType 属性详解及 Blob 与 ArrayBuffer 解析
前端·javascript·ajax
花花鱼44 分钟前
vue3 基于element-plus进行的一个可拖动改变导航与内容区域大小的简单方法
前端·javascript·elementui
k09331 小时前
sourceTree回滚版本到某次提交
开发语言·前端·javascript
EricWang13581 小时前
[OS] 项目三-2-proc.c: exit(int status)
服务器·c语言·前端
September_ning1 小时前
React.lazy() 懒加载
前端·react.js·前端框架
web行路人1 小时前
React中类组件和函数组件的理解和区别
前端·javascript·react.js·前端框架