大文件上传

问题:耗时久,网络断开导致上传失败
方案:利用分片上传,结合md5计算hash ,结合wenworker创建线程解决主线程阻塞
步骤:

1.分片:获取文件--获取尺寸--按照分片大小切割--利用md5计算出单个chunk的hash和整文件hash---创建formData实例 ,传给后端--利用promise.all全部传递成功调用后端完成接口实现

 handleInputFile = async() => {
    
    if (this.fileInput.current.files[0]) {    
      console.log(this.fileInput.current.files[0])
      const file = this.fileInput.current.files[0]
      let fileHash
      //利用webworker实现hash计算
      const worker = new Worker('/hash.js')
      worker.postMessage(file)
      worker.onmessage = (e) => {
        console.log(e.data)
        fileHash = e.data
      }
      const chunkSize = 1024  * 2
      const total = Math.ceil(file.size/chunkSize)
      const chunkArray = []
      for( let i = 0; i < total; i++){
        let start = i * chunkSize
        let end=Math.min(start + chunkSize, file.size)
        const fileInfo = file.slice(start,end)
        chunkArray.push(this.uploadChunk(fileInfo,i))
      }
      console.log(chunkArray)
      await promises.all(chunkArray)
      //调用全部上传成功标识接口
      try {
        await axios.post('http://localhost:3000/merge', {
            fileHash,
            total
        });
        console.log('文件合并完成');
      } catch (error) {
          console.error('文件合并出错:', error);
      }
    } else {
      this.setState({
        fileValue: null
      })
    }
  }
  uploadChunk = (fileInfo,i) => {
    let chunkhash 
    //利用webworker实现hash计算
    const worker = new Worker('/hash.js')
    worker.postMessage(fileInfo,i)
    worker.onmessage = (e) => {
      console.log(e.data)
      chunkhash = e.data
    }
    const formData = new FormData()
    formData.append('file', fileInfo)
    formData.append('chunkNumber', i)
    formData.append('chunkSize', 1024 * 2)
    formData.append('currentChunkSize', fileInfo.size)
    formData.append('chunkhash', chunkhash)

    try{
      //传给接口
      return new Promise((resolve,reject) => {
        const api = axios.create({
          baseURL: 'http://localhost:8080/',
          timeout: 10000,
          headers: {
            'Content-Type': 'multipart/form-data'
          }
        })
        api.post('/upload',formData).then((res) => {
          resolve(res)
        }).catch((err) => {})
      })
      
    }catch(error){
      console.log(error)
    }
  }
// 引入 md5 库
importScripts('https://cdnjs.cloudflare.com/ajax/libs/blueimp-md5/2.19.0/js/md5.min.js');

// 监听 message 事件,接收主线程发送的数据
self.onmessage = function (event) {
    const file = event.data;
    const reader = new FileReader();

    // 读取文件内容
    reader.readAsArrayBuffer(file);

    // 读取完成后计算 MD5 哈希值
    reader.onload = function () {
        const arrayBuffer = reader.result;
        const md5Hash = md5(arrayBuffer);
        // 将计算结果返回给主线程
        self.postMessage(md5Hash);
    };

    reader.onerror = function () {
        console.error('文件读取出错');
    };
};
可优化点:断点续传 放置在网络或者其他条件导致的上传失败
在每次上传前调用接口查询 哪些分片还没有上传成功
            
 <script>
        const fileInput = document.getElementById('fileInput');
        const uploadButton = document.getElementById('uploadButton');
        const progressBar = document.getElementById('progressBar');
        const progressText = document.getElementById('progressText');

        uploadButton.addEventListener('click', async () => {
            const file = fileInput.files[0];
            if (!file) return;

            const chunkSize = 1024 * 1024; // 每个文件块的大小为 1MB
            const totalChunks = Math.ceil(file.size / chunkSize);
            const fileHash = md5(file);
            let uploadedChunks = 0;

            // 检查已上传的文件块
            const response = await axios.get('http://localhost:3000/check', {
                params: {
                    fileHash
                }
            });
            const uploadedIndices = response.data;

            const uploadChunk = async (chunk, index) => {
                const chunkHash = md5(chunk);
                const formData = new FormData();
                formData.append('file', chunk);
                formData.append('chunkIndex', index);
                formData.append('totalChunks', totalChunks);
                formData.append('fileHash', fileHash);
                formData.append('chunkHash', chunkHash);

                try {
                    await axios.post('http://localhost:3000/upload', formData);
                    uploadedChunks++;
                    const progress = (uploadedChunks / totalChunks) * 100;
                    progressBar.style.width = `${progress}%`;
                    progressText.textContent = `上传进度: ${progress.toFixed(2)}%`;
                } catch (error) {
                    console.error('上传出错:', error);
                }
            };

            const promises = [];
            for (let i = 0; i < totalChunks; i++) {
                if (!uploadedIndices.includes(i)) {
                    const start = i * chunkSize;
                    const end = Math.min(start + chunkSize, file.size);
                    const chunk = file.slice(start, end);
                    promises.push(uploadChunk(chunk, i));
                }
            }

            await Promise.all(promises);

            // 所有文件块上传完成,发送合并请求
            try {
                await axios.post('http://localhost:3000/merge', {
                    fileHash,
                    totalChunks
                });
                console.log('文件合并完成');
            } catch (error) {
                console.error('文件合并出错:', error);
            }
        });
    </script>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值