Vue の大容量ファイルアップロードとブレークポイント再開アップロードの実装

Vue の大容量ファイルアップロードとブレークポイント再開アップロードの実装

ファイルアップロードのための2つのソリューション

ファイルストリーム(フォームデータ)に基づく

element-ui フレームワークのアップロード コンポーネントは、デフォルトでファイル ストリームに基づいています。

  • データ形式: form-data;
  • 転送されたデータ: ファイル ファイル ストリーム情報; ファイル名 ファイル名

クライアントはファイルをbase64に変換する

fileRead.readAsDataURL(file) を介して base64 文字列に変換した後、送信前に encodeURIComponent でコンパイルする必要があります。送信されたデータは qs.stringify によって処理され、リクエスト ヘッダーに "Content-Type": "application/x-www-form-urlencoded" が追加されます。

大きなファイルのアップロード

まず、element-ui を使用してページを構築します。アップロードの実装をカスタマイズするため、el-upload コンポーネントの auto-upload を false に設定する必要があります。action は必須のパラメータであり、ここで値を入力する必要はありません。

<テンプレート>
  <div id="アプリ">
    <!-- コンポーネントをアップロード -->
    <el-upload アクション ドラッグ :auto-upload="false" :show-file-list="false" :on-change="handleChange">
      <i class="el-icon-upload"></i>
      <div class="el-upload__text">ファイルをここにドラッグするか、<em>クリックしてアップロード</em></div>
      <div class="el-upload__tip" slot="tip">ビデオのサイズは 200M を超えません</div>
    </el-アップロード>

    <!-- 進行状況の表示-->
    <div class="progress-box">
      <span>アップロードの進行状況: {{ percent.toFixed() }}%</span>
      <el-button type="primary" size="mini" @click="handleClickBtn">{{ アップロード | btnTextFilter}}</el-button>
    </div>

    <!-- 正常にアップロードされたビデオを表示します-->
    <div v-if="ビデオURL">
      <video :src="videoUrl" コントロール />
    </div>
  </div>
</テンプレート>

ファイルオブジェクトを取得し、ArrayBufferオブジェクトに変換します。

後でハッシュ値とファイル名を生成するために SparkMD5 ライブラリを使用するため、ArrayBuffer に変換します。

非同期handleChange(ファイル) {
  定数 fileObj = file.raw
  試す{
    const バッファ = this.fileToBuffer(fileObj) を待機します
    console.log(バッファ)
  }キャッチ(e){
    コンソール.log(e)
  }
}

プリントバッファの結果は次のようになります


注意: before-upload 関数と on-change 関数の両方にパラメータとして file がありますが、on-change の file は File オブジェクトではありません。File オブジェクトを取得するには、file.raw を使用する必要があります。 ここでは、FileReader クラスを使用して、File オブジェクトを ArrayBuffer オブジェクトに変換します。これは非同期プロセスであるため、Promise でカプセル化されています。

// File オブジェクトを ArrayBuffer に変換します 
fileToBuffer(ファイル) {
  新しい Promise を返します ((resolve, reject) => {
    const fr = 新しい FileReader()
    fr.onload = e => {
      解決(e.target.result)
    }
    fr.readAsArrayBuffer(ファイル)
    fr.onerror = () => {
      拒否(新しいエラー('ファイル形式の変換エラー'))
    }
  })
}

スライスを作成する

ファイルは、固定サイズまたは固定数で複数の部分に分割できます。js で使用される IEEE754 バイナリ浮動小数点演算標準によって発生するエラーを回避するために、ファイルを固定サイズでカットし、各スライスのサイズを 2M に設定することにしました。つまり、2M = 21024KB = 21024*1024B = 2097152B です。 Blob.slice()はファイルを切り取るために使用されます

// ファイルを固定サイズ (2M) にスライスします。ここでは複数の定数が宣言されていることに注意してください。const chunkSize = 2097152,
  chunkList = [], // すべてのスライスを保持する配列 chunkListLength = Math.ceil(fileObj.size / chunkSize), // スライスの合計数を計算 suffix = /\.([0-9A-z]+)$/.exec(fileObj.name)[1] // ファイルサフィックス // ファイルの内容に基づいてハッシュ値を生成する const spark = new SparkMD5.ArrayBuffer()
spark.append(バッファ)
定数ハッシュ = spark.end()

// スライスを生成します。バックエンドでは、渡されるパラメータとしてバイトデータチャンク (chunk) と各データチャンクのファイル名 (fileName) が必要です。
let curChunk = 0 // スライス時の初期位置 for (let i = 0; i < chunkListLength; i++) {
  定数項目 = {
    チャンク: fileObj.slice(curChunk, curChunk + chunkSize),
    fileName: `${hash}_${i}.${suffix}` // ファイル名はhash_1.jpgに従って命名されます}
  curChunk += チャンクサイズ
  chunkList.push(アイテム)
}
console.log(チャンクリスト)

ファイルを選択すると、次のような印刷結果が表示されます。

リクエストを送信

リクエストの送信は並列またはシリアルで行うことができます。ここではシリアル送信を選択します。各スライスごとに新しいリクエストが作成されます。ブレークポイントの再開を実現するために、リクエストを関数 fn にカプセル化し、配列 requestList を使用してリクエスト セットを保存し、リクエスト送信用の send 関数をカプセル化します。このようにして、一時停止ボタンが押されると、アップロードを簡単に終了できます。コードは次のとおりです。

リクエストを送信します(){
  const requestList = [] // リクエストコレクション this.chunkList.forEach(item => {
    定数fn = () => {
      const フォームデータ = 新しいフォームデータ()
      formData.append('チャンク', item.チャンク)
      formData.append('ファイル名', item.fileName)
      戻り値: axios({
        URL: '/single3',
        メソッド: 'post'、
        ヘッダー: { 'Content-Type': 'multipart/form-data' },
        データ: フォームデータ
      }).then(res => {
        if (res.data.code === 0) { // 成功if (this.percentCount === 0) {
            this.パーセントカウント = 100 / this.チャンクリストの長さ
          }
          this.percent += this.percentCount // 進行状況を変更}
      })
    }
    リクエストリストをプッシュ(fn)
  })
  
  let i = 0 // 送信されたリクエストの数を記録する const send = async () => {
    // if ('pause') 戻り値
    if (i >= リクエストリストの長さ) {
      //完了したリターンを送信する
    } 
    リクエストリスト[i]()を待つ
    私は++
    送信()
  }
  send() // リクエストを送信する},

axios 部分は、次の形式で直接記述することもできます。

axios.post('/single3', フォームデータ, {
  ヘッダー: { 'Content-Type': 'multipart/form-data' }
})

すべてのスライスが正常に送信された後

バックエンドインターフェースに従って、別の get リクエストが送信され、ファイルのハッシュ値がサーバーに渡されます。これを実装するための完全なメソッドを定義します。ここでは、送信されるファイルはビデオファイルであると想定しています。

const 完全 = () => {
  アクシオス({
    URL: '/merge',
    メソッド: 'get'、
    パラメータ: { ハッシュ: this.hash }
  }).then(res => {
    if (res.data.code === 0) { // リクエストが正常に送信されました this.videoUrl = res.data.path
    }
  })
}

この方法では、ファイルが正常に送信された後、ページで送信されたビデオを閲覧できます。

履歴書のダウンロード

まず、一時停止ボタンのテキストが処理されます。フィルターが使用されます。アップロード値が true の場合は「一時停止」が表示され、それ以外の場合は「続行」が表示されます。

フィルター:
  btnTextFilter(val) {
    戻り値 ? '一時停止' : '続行'
  }
}

一時停止ボタンが押されると、handleClickBtnメソッドがトリガーされます。

ハンドルクリックボタン() {
  this.upload = !this.upload
  // 一時停止されていない場合はアップロードを続行します if (this.upload) this.sendRequest()
}

スライスを送信するには、send メソッドの先頭に if (!this.upload) return を追加します。これにより、アップロード変数が false である限り、アップロードは続行されません。一時停止後に送信を継続するには、スライスの送信が成功するたびにchunkList配列からスライスを削除する必要があります。this.chunkList.splice(index, 1)

コードの概要

<テンプレート>
  <div id="アプリ">
    <!-- コンポーネントをアップロード -->
    <el-upload アクション ドラッグ :auto-upload="false" :show-file-list="false" :on-change="handleChange">
      <i class="el-icon-upload"></i>
      <div class="el-upload__text">ファイルをここにドラッグするか、<em>クリックしてアップロード</em></div>
      <div class="el-upload__tip" slot="tip">ビデオのサイズは 200M を超えません</div>
    </el-アップロード>

    <!-- 進行状況の表示-->
    <div class="progress-box">
      <span>アップロードの進行状況: {{ percent.toFixed() }}%</span>
      <el-button type="primary" size="mini" @click="handleClickBtn">{{ アップロード | btnTextFilter}}</el-button>
    </div>

    <!-- 正常にアップロードされたビデオを表示します-->
    <div v-if="ビデオURL">
      <video :src="videoUrl" コントロール />
    </div>
  </div>
</テンプレート>

<スクリプト>
  「spark-md5」からSparkMD5をインポートします。
  「axios」からaxiosをインポートします
  
  エクスポートデフォルト{
    名前: 'App3'、
    フィルター:
      btnTextFilter(val) {
        戻り値 ? '一時停止' : '続行'
      }
    },
    データ() {
      戻る {
        パーセント: 0,
        ビデオURL: ''、
        アップロード: true、
        パーセントカウント: 0
      }
    },
    メソッド: {
      非同期handleChange(ファイル) {
        if (!file) 戻り値
        パーセント = 0
        this.videoUrl = ''
        // ファイルを取得してArrayBufferオブジェクトに変換します。const fileObj = file.raw
        バッファリング
        試す {
          バッファ = this.fileToBuffer(fileObj) を待機します
        } キャッチ (e) {
          コンソール.log(e)
        }
        
        // ファイルを固定サイズ (2M) にスライスします。ここでは複数の定数が宣言されていることに注意してください。const chunkSize = 2097152,
          chunkList = [], // すべてのスライスを保持する配列 chunkListLength = Math.ceil(fileObj.size / chunkSize), // スライスの合計数を計算 suffix = /\.([0-9A-z]+)$/.exec(fileObj.name)[1] // ファイルサフィックス // ファイルの内容に基づいてハッシュ値を生成する const spark = new SparkMD5.ArrayBuffer()
        spark.append(バッファ)
        定数ハッシュ = spark.end()

        // スライスを生成します。バックエンドでは、渡されるパラメータとしてバイトデータチャンク (chunk) と各データチャンクのファイル名 (fileName) が必要です。
        let curChunk = 0 // スライス時の初期位置 for (let i = 0; i < chunkListLength; i++) {
          定数項目 = {
            チャンク: fileObj.slice(curChunk, curChunk + chunkSize),
            fileName: `${hash}_${i}.${suffix}` // ファイル名はhash_1.jpgに従って命名されます}
          curChunk += チャンクサイズ
          chunkList.push(アイテム)
        }
        this.chunkList = chunkList // sendRequest は this.hash = hash を使用する必要があります // sendRequest は this.sendRequest() を使用する必要があります
      },
      
      // リクエストを送信する sendRequest() {
        const requestList = [] // リクエストコレクション this.chunkList.forEach((item, index) => {
          定数fn = () => {
            const フォームデータ = 新しいフォームデータ()
            formData.append('チャンク', item.チャンク)
            formData.append('ファイル名', item.fileName)
            戻り値: axios({
              URL: '/single3',
              メソッド: 'post'、
              ヘッダー: { 'Content-Type': 'multipart/form-data' },
              データ: フォームデータ
            }).then(res => {
              if (res.data.code === 0) { // Successif (this.percentCount === 0) { // アップロードが成功した後にスライスを削除し、chunkList の長さを変更して percentCount の値に影響を与えないようにしますthis.percentCount = 100 / this.chunkList.length
                }
                this.percent += this.percentCount // 進行状況を変更します this.chunkList.splice(index, 1) // アップロードが成功したら、ブレークポイントの再開を容易にするためにこのチャンクを削除します}
            })
          }
          リクエストリストをプッシュ(fn)
        })
        
        let i = 0 // 送信されたリクエストの数を記録する // すべてのファイルスライスが送信されたら、'/merge' インターフェースを要求し、ファイルハッシュをサーバーに渡す必要があります const complete = () => {
          アクシオス({
            URL: '/merge',
            メソッド: 'get'、
            パラメータ: { ハッシュ: this.hash }
          }).then(res => {
            if (res.data.code === 0) { // リクエストが正常に送信されました this.videoUrl = res.data.path
            }
          })
        }
        const send = 非同期() => {
          if (!this.upload) 戻り値
          if (i >= リクエストリストの長さ) {
            // 送信完了()
            戻る
          } 
          リクエストリスト[i]()を待つ
          私は++
          送信()
        }
        send() // リクエストを送信する},
      
      // 一時停止ボタンを押す handleClickBtn() {
        this.upload = !this.upload
        // 一時停止されていない場合はアップロードを続行します if (this.upload) this.sendRequest()
      },
      
      // File オブジェクトを ArrayBuffer に変換します 
      fileToBuffer(ファイル) {
        新しい Promise を返します ((resolve, reject) => {
          const fr = 新しい FileReader()
          fr.onload = e => {
            解決(e.target.result)
          }
          fr.readAsArrayBuffer(ファイル)
          fr.onerror = () => {
            拒否(新しいエラー('ファイル形式の変換エラー'))
          }
        })
      }
    }
  }
</スクリプト>

<スタイルスコープ>
  .progress-box {
    ボックスのサイズ: 境界線ボックス;
    幅: 360ピクセル;
    ディスプレイ: フレックス;
    コンテンツの両端揃え: スペースの間;
    アイテムの位置を中央揃えにします。
    上マージン: 10px;
    パディング: 8px 10px;
    背景色: #ecf5ff;
    フォントサイズ: 14px;
    境界線の半径: 4px;
  }
</スタイル>

効果は以下のとおりです。

もう一つ

フォームデータ

ここでデータを送信するには FormData を使用します。エンコード タイプが "multipart/form-data" に設定されている場合、フォームと同じ形式が使用されます。

フォームデータの追加()

FormData オブジェクト内の既存のキーに新しい値が追加されるか、キーが存在しない場合はキーが追加されます。このメソッドは、formData.append(name, value, filename) の 3 つのパラメータを渡すことができます。ここで、filename はオプションのパラメータであり、サーバーに渡されるファイル名です。2 番目のパラメータとして Blob または File が使用される場合、Blob オブジェクトのデフォルトのファイル名は "blob" になります。 File オブジェクトのデフォルトのファイル名は、ファイルの名前です。

Vue の大容量ファイルアップロードとブレークポイント再開の実装に関するこの記事はこれで終わりです。Vue の大容量ファイルアップロードとブレークポイント再開の関連コンテンツについては、123WORDPRESS.COM の以前の記事を検索するか、次の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • Vue+element+oss はフロントエンドのフラグメントアップロードとブレークポイント再開を実現します
  • vue-simple-uploader をベースに、ファイルセグメントアップロード、インスタントアップロード、ブレークポイント再開のグローバルアップロードプラグイン機能をカプセル化します。

<<:  mysql-8.0.15-winx64 解凍バージョンのインストールチュートリアルと終了する 3 つの方法

>>:  仮想マシンを作成し、VMware に Redhat Linux オペレーティング システムをインストールする (グラフィック チュートリアル)

推薦する

Reactマウスの複数選択機能の設定方法

一般的に、リストには選択機能があり、単一選択、二重選択、複数選択が非常に一般的です。カスタム ループ...

Centos7 DockerでNginxファイルを変更するプロセスの詳細な説明

1. docker に nginx をインストールします。 docker に Nginx をインスト...

Vue+Element UIはドロップダウンメニューのカプセル化を実現します

この記事の例では、ドロップダウンメニューのカプセル化を実装するためのVue + Element UI...

CSS で雨滴アニメーション効果を実装するサンプルコード

ガラス窓今日実現するのは雨滴効果です。ただし、雨滴効果を実現する前に、まずは曇りガラス効果を作成しま...

最高の無料英語フォント33選を紹介

チャンクファイブフリータイプファミリーCuprum JAH I フリーフォントイェセヴァブークレフィ...

React 並行関数エクスペリエンス (フロントエンド並行モード)

React は、開発者が Web およびモバイルベースのアプリケーションを作成するために使用するオ...

4 つの主要な SQL ランキング関数 ROW_NUMBER、RANK、DENSE_RANK、NTILE の使用方法の紹介

1. ROW_NUMBER()定義: ROW_NUMBER() 関数は、select によってクエリ...

MySQL 8.0.22 圧縮パッケージの完全なインストールと構成のチュートリアル図 (テスト済みで効果的)

1. zipインストールパッケージをダウンロードするMySQL サーバー 8.0.22 の圧縮パッ...

XHTML Web ページ チュートリアル

<br />この記事は主に、初心者にXHTMLの基本知識と、XHTMLとHTMLの違いを...

nestjs における例外フィルター Exceptionfilter の具体的な使用法

Nestjs 例外フィルターといえば、非常に強力な .Net のグローバル フィルターについて触れな...

VUE+SpringBootはページング機能を実装します

この記事では主に、Vue + SpringBoot でページ分割されたリストデータを実装する方法を紹...

docker ベースの redis-sentinel クラスターの構築例

1. 概要Redis Cluster は、Redis ノードのグループ間での高可用性とシャーディング...

WeChat アプレットカスタムタブバーステップ記録

目次1. はじめに2. タブバーのスタイルをカスタマイズする3. カスタムタブバーと関連設定を導入す...

Linux システムに Zookeeper サービスをインストールする方法

1. /usr/local/services/zookeeper フォルダを作成します。 mkdir...

MySQL サービス 1067 エラーの解決策: mysql 実行可能ファイルのパスを変更する

今日、MySQLサービス1067エラー問題に遭遇しました。システムアカウントを使用するように設定して...