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 オペレーティング システムをインストールする (グラフィック チュートリアル)

推薦する

Centos7 に MySQL 8.0.23 をインストールする手順 (初心者レベル)

まず、MySQL とは何かを簡単に紹介します。簡単に言えば、データベースはデータを格納するための倉庫...

MySQL ストアド プロシージャの使用例の分析

この記事では、MySQL ストアド プロシージャの使用方法について説明します。ご参考までに、詳細は以...

アリババの中秋節ロゴとウェブサイトのデザインプロセス

<br />まずアイデアを考え、次にスケッチを描き、次にマウスでスケッチし、最後にフラッ...

サブメニューをクリックする効果を実現するJavaScript

この記事では、クリック時にサブメニューを表示するためのJavaScriptの具体的なコードを参考まで...

MySQL データベースの必須条件クエリ ステートメント

目次1. 基本的な文法2. 条件式によるフィルタリング3. 論理式によるフィルタリング4. あいまい...

Centos7 ベースの Nginx Web サイト サーバーの構築の詳細説明 (仮想 Web ホストの構成を含む)

1. Nginx サービス基盤Nginx (エンジン x) は、パフォーマンスの最適化のために特別...

複数のドメイン名に対する Nginx リバース プロキシを使用した HTTP および HTTPS サービスの実装

現在、Nginx は、Web サービスを提供するために、Windows ベースの IIS と Lin...

Linux インストール MongoDB の起動と一般的な問題の解決

MongoDB のインストール プロセスと問題記録1. MongoDBのインストールMongoDBを...

Ubuntu の起動後にアプリケーションを実行するためのターミナルの設定方法

1.メニューバーにスタートと入力し、スタートアップアプリケーションをクリックして入力します。 2. ...

Vueにおける仮想DOMの理解のまとめ

これは本質的に、ビュー インターフェース構造を記述するために使用される共通の js オブジェクトです...

IE ブラウザの HTML ハック タグの概要

コードをコピーコードは次のとおりです。 <!--[if !IE]><!-->...

JavaScriptの基礎を学ぶ

目次1. JavaScriptを記述する場所2. JavaScriptでよく使われる入力文と出力文1...

Alibaba Cloud CentOS7 サーバーの nginx 構成と FAQ の分析

序文:この記事は、jackyzm のブログ https://www.cnblogs.com/jack...

vue-router のハッシュモードと履歴モードの違い

vue-routerには2つのモードがありますハッシュモード履歴モード1. シングルページアプリケー...

Vue で円形プログレスバーを実装する例

データ表示は、常にあらゆる職業の人々が求めているものです。特にフロントエンド開発業界では、データを表...