欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JavaScript實(shí)現(xiàn)大文件分片上傳處理

 更新時間:2021年08月01日 13:19:25   作者:小小熱愛  
這篇文章主要為大家詳細(xì)介紹了JavaScript實(shí)現(xiàn)大文件分片上傳處理,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

很多時候我們在處理文件上傳時,如視頻文件,小則幾十M,大則 1G+,以一般的HTTP請求發(fā)送數(shù)據(jù)的方式的話,會遇到的問題:

1、文件過大,超出服務(wù)端的請求大小限制;
2、請求時間過長,請求超時;
3、傳輸中斷,必須重新上傳導(dǎo)致前功盡棄

這些問題很影響用戶的體驗(yàn)感,所以下面介紹一種基于原生JavaScript進(jìn)行文件分片處理上傳的方案,具體實(shí)現(xiàn)過程如下:

1、通過dom獲取文件對象,并且對文件進(jìn)行MD5加密(文件內(nèi)容+文件標(biāo)題形式),采用SparkMD5進(jìn)行文件加密;
2、進(jìn)行分片設(shè)置,文件File基于Blob, 繼承了Blob的功能,可以把File當(dāng)成Blob的子類,利于Blob的slice方法進(jìn)行文件分片處理,并且依次進(jìn)行上傳
3、分片文件上傳完成后,請求合并接口后端進(jìn)行文件合并處理即可

1. 上傳文件頁面

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>文件上傳</title>
  <script src="https://cdn.bootcss.com/axios/0.18.0/axios.min.js"></script>
  <script src="https://code.jquery.com/jquery-3.4.1.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/spark-md5/3.0.0/spark-md5.js"></script>
  <style>
    /* 自定義進(jìn)度條樣式 */
    .precent input[type=range] {
      -webkit-appearance: none;
      /*清除系統(tǒng)默認(rèn)樣式*/
      width: 7.8rem;
      /* background: -webkit-linear-gradient(#ddd, #ddd) no-repeat, #ddd; */
      /*設(shè)置左邊顏色為#61bd12,右邊顏色為#ddd*/
      background-size: 75% 100%;
      /*設(shè)置左右寬度比例*/
      height: 0.6rem;
      /*橫條的高度*/
      border-radius: 0.4rem;
      border: 1px solid #ddd;
      box-shadow: 0 0 10px rgba(0,0,0,.125) inset ;
    }

    /*拖動塊的樣式*/
    .precent input[type=range]::-webkit-slider-thumb {
      -webkit-appearance: none;
      /*清除系統(tǒng)默認(rèn)樣式*/
      height: .9rem;
      /*拖動塊高度*/
      width: .9rem;
      /*拖動塊寬度*/
      background: #fff;
      /*拖動塊背景*/
      border-radius: 50%;
      /*外觀設(shè)置為圓形*/
      border: solid 1px #ddd;
      /*設(shè)置邊框*/
    }

  </style>
</head>

<body>
  <h1>大文件分片上傳測試</h1>
  <div>
    <input id="file" type="file" name="avatar" />
    <div style="padding: 10px 0;">
      <input id="submitBtn" type="button" value="提交" />
      <input id="pauseBtn" type="button" value="暫停" />
    </div>
    <div class="precent">
      <input type="range" value="0" /><span id="precentVal">0%</span>
    </div>
  </div>
  <script type="text/javascript" src="./js/index.js"></script>
</body>

</html>

2. 大文件分片上傳處理

$(document).ready(() => {
  const submitBtn = $('#submitBtn');  //提交按鈕
  const precentDom = $(".precent input")[0]; // 進(jìn)度條
  const precentVal = $("#precentVal");  // 進(jìn)度條值對應(yīng)dom
  const pauseBtn = $('#pauseBtn');  // 暫停按鈕
  // 每個chunk的大小,設(shè)置為1兆
  const chunkSize = 1 * 1024 * 1024;
  // 獲取slice方法,做兼容處理
  const blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
  // 對文件進(jìn)行MD5加密(文件內(nèi)容+文件標(biāo)題形式)
  const hashFile = (file) => {
    return new Promise((resolve, reject) => {
      const chunks = Math.ceil(file.size / chunkSize);
      let currentChunk = 0;
      const spark = new SparkMD5.ArrayBuffer();
      const fileReader = new FileReader();
      function loadNext() {
        const start = currentChunk * chunkSize;
        const end = start + chunkSize >= file.size ? file.size : start + chunkSize;
        fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));
      }
      fileReader.onload = e => {
        spark.append(e.target.result); // Append array buffer
        currentChunk += 1;
        if (currentChunk < chunks) {
          loadNext();
        } else {
          console.log('finished loading');
          const result = spark.end();
          // 通過內(nèi)容和文件名稱進(jìn)行md5加密
          const sparkMd5 = new SparkMD5();
          sparkMd5.append(result);
          sparkMd5.append(file.name);
          const hexHash = sparkMd5.end();
          resolve(hexHash);
        }
      };
      fileReader.onerror = () => {
        console.warn('文件讀取失?。?);
      };
      loadNext();
    }).catch(err => {
      console.log(err);
    });
  }

  // 提交
  submitBtn.on('click', async () => {
    var pauseStatus = false;
    var nowUploadNums = 0
    // 1.讀取文件
    const fileDom = $('#file')[0];
    const files = fileDom.files;
    const file = files[0];
    if (!file) {
      alert('沒有獲取文件');
      return;
    }
    // 2.設(shè)置分片參數(shù)屬性、獲取文件MD5值
    const hash = await hashFile(file); //文件 hash 
    const blockCount = Math.ceil(file.size / chunkSize); // 分片總數(shù)
    const axiosPromiseArray = []; // axiosPromise數(shù)組
    // 文件上傳
    const uploadFile = () => {
      const start = nowUploadNums * chunkSize;
      const end = Math.min(file.size, start + chunkSize);
      // 構(gòu)建表單
      const form = new FormData();
      // blobSlice.call(file, start, end)方法是用于進(jìn)行文件分片
      form.append('file', blobSlice.call(file, start, end));
      form.append('index', nowUploadNums);
      form.append('hash', hash);
      // ajax提交 分片,此時 content-type 為 multipart/form-data
      const axiosOptions = {
        onUploadProgress: e => {
          nowUploadNums++;
          // 判斷分片是否上傳完成
          if (nowUploadNums < blockCount) {
            setPrecent(nowUploadNums, blockCount);
            uploadFile(nowUploadNums)
          } else {
            // 4.所有分片上傳后,請求合并分片文件
            axios.all(axiosPromiseArray).then(() => {
              setPrecent(blockCount, blockCount); // 全部上傳完成
              axios.post('/file/merge_chunks', {
                name: file.name,
                total: blockCount,
                hash
              }).then(res => {
                console.log(res.data, file);
                pauseStatus = false;
                alert('上傳成功');
              }).catch(err => {
                console.log(err);
              });
            });
          }
        },
      };
      // 加入到 Promise 數(shù)組中
      if (!pauseStatus) {
        axiosPromiseArray.push(axios.post('/file/upload', form, axiosOptions));
      }

    }
    // 設(shè)置進(jìn)度條
    function setPrecent(now, total) {
      var prencentValue = ((now / total) * 100).toFixed(2)
      precentDom.value = prencentValue
      precentVal.text(prencentValue + '%')
      precentDom.style.cssText = `background:-webkit-linear-gradient(top, #059CFA, #059CFA) 0% 0% / ${prencentValue}% 100% no-repeat`
    }
    // 暫停
    pauseBtn.on('click', (e) => {
      pauseStatus = !pauseStatus;
      e.currentTarget.value = pauseStatus ? '開始' : '暫停'
      if (!pauseStatus) {
        uploadFile(nowUploadNums)
      }
    })
    uploadFile();
  });
})

3. 文件上傳和合并分片文件接口(node)

const Router = require('koa-router');
const multer = require('koa-multer');
const fs = require('fs-extra');
const path = require('path');
const router = new Router();

const { mkdirsSync } = require('../utils/dir');
const uploadPath = path.join(__dirname, 'upload');
const chunkUploadPath = path.join(uploadPath, 'temp');
const upload = multer({ dest: chunkUploadPath });

// 文件上傳接口
router.post('/file/upload', upload.single('file'), async (ctx, next) => {
  const { index, hash } = ctx.req.body;
  const chunksPath = path.join(chunkUploadPath, hash, '/');
  if(!fs.existsSync(chunksPath)) mkdirsSync(chunksPath);
  fs.renameSync(ctx.req.file.path, chunksPath + hash + '-' + index);
  ctx.status = 200;
  ctx.res.end('Success');
}) 
// 合并分片文件接口
router.post('/file/merge_chunks', async (ctx, next) => {
  const { name, total, hash } = ctx.request.body;
  const chunksPath = path.join(chunkUploadPath, hash, '/');
  const filePath = path.join(uploadPath, name);
  // 讀取所有的chunks
  const chunks = fs.readdirSync(chunksPath);
  // 創(chuàng)建存儲文件
  fs.writeFileSync(filePath, ''); 
  if(chunks.length !== total || chunks.length === 0) {
    ctx.status = 200;
    ctx.res.end('切片文件數(shù)量不符合');
    return;
  }
  for (let i = 0; i < total; i++) {
    // 追加寫入到文件中
    fs.appendFileSync(filePath, fs.readFileSync(chunksPath + hash + '-' +i));
    // 刪除本次使用的chunk    
    fs.unlinkSync(chunksPath + hash + '-' +i);
  }
  fs.rmdirSync(chunksPath);
  // 文件合并成功,可以把文件信息進(jìn)行入庫。
  ctx.status = 200;
  ctx.res.end('Success');
})

以上就是文件分片上傳的基本過程,過程中加入了上傳進(jìn)度條、暫停和開始上傳操作,見詳細(xì)代碼

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • JavaScript程序員應(yīng)該知道的45個實(shí)用技巧

    JavaScript程序員應(yīng)該知道的45個實(shí)用技巧

    在這篇文章中,我將分享一組JavaScript的技巧、竅門和最佳實(shí)踐,這些都是JavaScript程序員應(yīng)該知曉的,不管他們是使用在瀏覽器/引擎上,還是服務(wù)器端(SSJS——Service Side JavaScript)JavaScript解釋器上
    2014-03-03
  • JS中操作JSON總結(jié)

    JS中操作JSON總結(jié)

    本篇文章主要是對JS操作JSON進(jìn)行了總結(jié)介紹,需要的朋友可以過來參考下,希望對大家有所幫助
    2014-02-02
  • 詳解webpack解惑:require的五種用法

    詳解webpack解惑:require的五種用法

    這篇文章主要介紹了詳解webpack解惑:require的五種用法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-06-06
  • 常用的JavaScript WEB操作方法分享

    常用的JavaScript WEB操作方法分享

    這篇文章主要介紹了常用的JavaScript WEB操作方法分享,包含數(shù)組方法集、cookie方法集、url方法集、正則表達(dá)式方法集、字符串方法集、加密方法集、日期方法集等常用操作方法,需要的朋友可以參考下
    2015-02-02
  • 《javascript設(shè)計(jì)模式》學(xué)習(xí)筆記三:Javascript面向?qū)ο蟪绦蛟O(shè)計(jì)單例模式原理與實(shí)現(xiàn)方法分析

    《javascript設(shè)計(jì)模式》學(xué)習(xí)筆記三:Javascript面向?qū)ο蟪绦蛟O(shè)計(jì)單例模式原理與實(shí)現(xiàn)方法分析

    這篇文章主要介紹了Javascript面向?qū)ο蟪绦蛟O(shè)計(jì)單例模式原理與實(shí)現(xiàn)方法,結(jié)合實(shí)例形式分析了《javascript設(shè)計(jì)模式》中Javascript面向?qū)ο髥卫J较嚓P(guān)概念、原理、用法及操作注意事項(xiàng),需要的朋友可以參考下
    2020-04-04
  • 詳解Webpack4多頁應(yīng)用打包方案

    詳解Webpack4多頁應(yīng)用打包方案

    這篇文章主要介紹了詳解Webpack4多頁應(yīng)用打包方案,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • JavaScript使用cookie

    JavaScript使用cookie

    JavaScript使用cookie...
    2007-02-02
  • 最新評論