likes
comments
collection
share

文件名称重复怎么进行上传限制

作者站长头像
站长
· 阅读数 11

前言

前面文章实现了如何优雅的判断文件格式,现在我们来实现如果文件名称重复怎么进行上传?

文章地址:juejin.cn/post/708624…

md5进行hash计算出来的值可以实现不重复上传文件名称,但是如果文件过大的时候hash值计算过慢也是会影响浏览器卡顿,那么怎么进行优化呢?

xdm,接下来我们一起探寻文件上传的奥秘吧!!!

实现

  1. 使用web worker进行计算

    • npm 安装包 spark-md5,spark-md5.min.js单独抽离出来进行使用

    • 创建hash.js进行后台计算

      self.importScripts('spark-md5.min.js')
      ​
      self.onmessage = e => {
        const { chunks } = e.data
        const spark = new self.SparkMD5.ArrayBuffer()
      ​
        let count = 0
      ​
        const loadNext = index => {
          const reader = new FileReader()
          reader.readAsArrayBuffer(chunks[index].file)
          reader.onload = e => {
            count++
      ​
            spark.append(e.target.result)
      ​
            if (count === chunks.length) {
              self.postMessage({
                hash: spark.end()
              })
            } else {
              loadNext(count)
            }
          }
        }
        loadNext(0)
      }
      
    • 使用worker执行计算

      function calculateHashWorker() {
        return new Promise(resolve => {
          worker = new Worker('./static/hash.js')
          worker.postMessage({
            chunks: chunks
          })
          worker.onmessage = e => {
            console.log(e.data)
            const { hash } = e.data;
            if (hash) {
              resolve(hash)
            }
          }
        })
      }
      
    • 最后得到最终的hash值。

  2. 使用requestIdleCallback浏览器空闲时间进行计算

    async function calculateHashIdle() {
      return new Promise(resolve => {
        const spark = new self.SparkMD5.ArrayBuffer()
        let count = 0
    ​
        const appendToSpark = async file => {
          return new Promise(resolve => {
            const reader = new FileReader()
            reader.readAsArrayBuffer(file)
            reader.onload = e => {
              spark.append(e.target.result)
              resolve()
            }
          })
        }
    ​
        const wookLoop = async deadline => {
          while (count < chunks.length && deadline.timeRemaining() > 1) {
            // 空闲时间,有任务
            await appendToSpark(chunks[count].file)
            count++
            if (count < chunks.length) {
    ​
            } else {
              resolve(spark.end())
            }
          }
          window.requestIdleCallback(wookLoop)
        }
        window.requestIdleCallback(wookLoop)
      })
    }
    
    • 最终也可以快速不失卡顿的进行hash的计算。
  3. 抽样hash进行计算,hash值取前中后三块区域进行计算。前中后并且取2个字节进行计算,速度就会很快,但是安全性就会变低,酌情处理。

    async function calculateHashSample() {
      return new Promise(resolve => {
        const spark = new self.SparkMD5.ArrayBuffer()
        const reader = new FileReader()
    ​
        const size = file.size
        const offset = 2 * 1024 * 1024
        // 第一个2M,最后一个区块数据全要
        let chunks = [file.slice(0, offset)]
    ​
        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
        }
        // 中间的,取前中后各2个字节
        reader.readAsArrayBuffer(new Blob(chunks))
        reader.onload = e => {
          spark.append(e.target.result)
          resolve(spark.end())
        }
      })
    }
    

结语

上面通过三种不同的方式让我们在解决文件过大的时候hash值计算过慢造成浏览器卡顿的问题上有了解决方式。

使用webwork进行计算,需要单独使用hash文件进行后台计算。

使用requestIdleCallback进行计算,可以很好的利用浏览器的空闲时间进行计算(比较推荐)。

使用抽样hash进行计算的会比上面两种都快,但是由于不是全部计算安全性就比较低了,适用于一些对安全性要求比较低的需求。