zoukankan      html  css  js  c++  java
  • md5 文件上传

    md5 文件上传

    当用户在操作文件上传功能时,某些文件特别大,比如:100M,1G ?G 。网速慢,浏览器卡顿,可使用文件切片方式上传。

    html 页面

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>md5 文件上传</title>
      <style>
        .loading {
           300px;
          border: 1px solid #333;
          height: 20px;
        }
        .loading div {
           0%;
          background: #F00;
          height: 20px;
        }
        
      </style>
    </head>
    <body>
      <input type="file">
      <div id="loading" class="loading"><div></div></div>
      <!-- md5 文件 网上可下载 -->
      <script src="./md5.js"></script>
      <script>
        const fileEl = document.querySelector('input')
        const loading = document.querySelector('#loading > div')
        const chunkSize = 1024 * 1024* 2;
        fileEl.onchange = async (e) => {
          const ele = e.target;
          const file = ele.files[0];
          // 文件分片
          const chunks = createFileChunk(file, chunkSize)
          
          // 判断文件是否重复
          const hash = await calculateHashSample(file);
          // 1、文件上传
          await calculateHashWorker(chunks)
          // 2、利用浏览器空闲时间上传
          // await calculateHashIdle(chunks)
          const data = chunks.map((chunk, index)=> {
            return {
              hash,
              index,
              chunk: chunk.file,
              progress: 0
            }
          })
          console.log(data)
        }
      </script>
    </body>
    </html>
    

    文件切片

      function createFileChunk(file, size) {
        const chunks = [];
        let cur = 0;
        while (cur < file.size) {
          chunks.push({
            index: cur,
            file: file.slice(cur, cur + size) // 文件切割
          })
          cur += size;
        }
        // 返回切分完成后的文件
        return chunks;
      }
    

    文件上传

    FileReader() 对象允许Web应用程序异步读取存储在用户计算机上的文件或原始数据缓冲区)的内容,使用 FileBlob 对象指定要读取的文件或数据。

     async function calculateHashWorker(chunks) {
        return new Promise(reslove => {
          let count = 0;
          const spark = new SparkMD5.ArrayBuffer();
          let progress = 0;
          const loadNext = index => {
            const reader = new FileReader();
            reader.onload = e => {
              count++;
              spark.append(e.target.result)
              if(count === chunks.length) {
                spark.end();
                console.log('完成')
                loading.style.width = (100) + '%';
                reslove();
              } else {
                progress += 100 / chunks.length; // 进度条
                console.log('上传中...',progress)
                loading.style.width = (progress) + '%';
                // 进行下一步上传
                loadNext(count);
              }
            }
            reader.readAsArrayBuffer(chunks[count].file)
          }
          loadNext(0);
    
        })
      }
    

    利用空闲时间上传

    window.requestIdleCallback()方法将在浏览器的空闲时段内调用的函数排队。这使开发者能够在主事件循环上执行后台和低优先级工作,而不会影响延迟关键事件,如动画和输入响应。函数一般会按先进先调用的顺序执行,然而,如果回调函数指定了执行超时时间timeout,则有可能为了在超时前执行函数而打乱执行顺序。

      async function calculateHashIdle(chunks) {
        return new Promise(reslove => {
          const spark = new SparkMD5.ArrayBuffer();
          let count = 0;
          const appendToSpark = async file => {
            return new Promise(reslove => {
              const reader = new FileReader();
              reader.readAsArrayBuffer(file)
              reader.onload = e => {
                spark.append(e.target.result)
                reslove();
              }
            })
          }
    
          const workLoop = async deadline => {
            // 判断当前文件片段是否上传完成 &&  当前剩余时间大于 1
            // deadline.timeRemaining() 获取当前帧的剩余时间
            while (count < chunks.length && deadline.timeRemaining() > 1) {
              await appendToSpark(chunks[count].file)
              count++;
              if(count < chunks.length) {
                // 加载动画
                console.log('加载动画...',((100 * count) / chunks.length).toFixed(2) + '%');
                loading.style.width = ((100 * count) / chunks.length).toFixed(2) + '%';
    
              } else {
                console.log('加载完毕')
                loading.style.width = '100%';
                reslove(spark.end())
              }
            }
            window.requestIdleCallback(workLoop)
          }
          // 浏览器一旦空闲,调用 workLoop
          window.requestIdleCallback(workLoop)
        })
      }
    

    判断文件是否存在

    new Blob() 对象表示一个不可变、原始数据的类文件对象。它的数据可以按文本或二进制的格式进行读取,也可以转换成 ReadableStream 来用于数据操作

      // 采用抽样逻辑 比如: 1G 的文件,抽样后5M以内
      // 两个文件hash 一样,可能文件不一样,hash 不一样,文件一定不一样。
      async function calculateHashSample(file) {
        // 抽样
        return new Promise(reslove => {
          const spark = new SparkMD5.ArrayBuffer();
          const reader = new FileReader();
          const size = file.size;
          let offset = 1024 * 1024* 2;
          let chunks = [file.slice(0, offset)];//第一个2M,最后一个区块数据全要
    
          let cur = offset;
    
          while(cur < size) {
            if(cur + offset >= size) { 
              // 最后一个区块
              chunks.push(file.slice(cur, cur+offset))
            } else {
              // 中间区块
              const mid = cur + offset / 2;
              const end = cur + offset;
              chunks.push(file.slice(cur, cur + 2)) // 起始位置,取两个字节
              chunks.push(file.slice(mid, mid + 2)) // 中间位置,取两个字节
              chunks.push(file.slice(end - 2, end)) // 最后位置,取两个字节
            }
            cur += offset;
          }
    
          reader.onload = e => {
            spark.append(e.target.result)
            console.log('完成')
            reslove(spark.end());
          }
          reader.readAsArrayBuffer(new Blob(chunks))
        })
      }
    

    注意:坏处:抽样逻辑可能会出现误判概率低,,好处:可以提高效率

  • 相关阅读:
    数论分块与整除相
    P3254——DP&&入门
    P3384——树链剖分&&模板
    树链剖分模板
    P4145——线段树点修改&&模板题
    P1198最大数——线段树点修改&&模板题
    线段树(四)——两个标记(add和set)
    epoll 知识总结
    C++中的mutable关键字
    [LeetCode] Max Points on a Line
  • 原文地址:https://www.cnblogs.com/wangyong1997/p/13999882.html
Copyright © 2011-2022 走看看