vue-simple-uploader をベースに、ファイルセグメントアップロード、インスタントアップロード、ブレークポイント再開のグローバルアップロードプラグイン機能をカプセル化します。

vue-simple-uploader をベースに、ファイルセグメントアップロード、インスタントアップロード、ブレークポイント再開のグローバルアップロードプラグイン機能をカプセル化します。

1. はじめに

以前、同社は管理システムにグローバルアップロードプラグインを作成したいと考えていました。つまり、ページを切り替えてもアップロードインターフェースはそのまま残り、アップロードには影響しません。これは、Vue などの SPA フレームワークの前では問題になりません。しかし、バックエンドのボスは、マルチパートアップロード、インスタントアップロード、ブレークポイント再開アップロードの機能を実装する必要があると言っており、これは非常に困難に思えます。

以前、webuploader に関する記事を書きましたが、使用中に多くの問題が見つかり、公式チームはこのプラグインをメンテナンスしなくなりました。何日もの研究と失敗を経て、最終的に、プロジェクトでの使用に苦労せず安定しているvue-simple-uploaderプラグインに基づいてこの機能を実装することにしました。

基本的な(カスタマイズされていない)アップロード機能だけを実装したい場合は、 vue-simple-uploaderを直接使用し、そのドキュメントを読んでください。二次的なパッケージ化は不要です。
グローバルアップロードプラグインを実装したいだけの場合は、私の実装も参照してください。
マルチパートアップロード、インスタントアップロード、ブレークポイント再開などの複雑な機能を使用したことがある場合は、おめでとうございます。これがこの記事の焦点です。

この記事のソースコードはここにあります: https://github.com/shady-xia/Blog/tree/master/vue-simple-uploader

2. vue-simple-uploaderについて

vue-simple-uploader simple-uploader.jsに基づいてカプセル化された vue アップロード プラグインです。その利点には、以下のものが含まれますが、これらに限定されません。

  • ファイル、複数ファイル、フォルダのアップロードをサポート。ドラッグアンドドロップによるファイルとフォルダのアップロードをサポート
  • アップロードを一時停止して再開する
  • エラー処理
  • ファイルを介してサーバーがすでに存在するかどうかを判断することで実現できる「インスタント転送」をサポートします。
  • 複数パートのアップロード
  • 進捗状況、推定残り時間、エラー発生時の自動再試行、再送信などの操作をサポート

この記事を読む前に、まずsimple-uploader.jsのドキュメントを読んでから、 vue-simple-uploaderのドキュメントを読んで、各パラメータの役割を理解することをお勧めします。ここでは皆さんすでにご存知だと思います。 。
vue-simple-uploader ドキュメント

simple-uploader.js ドキュメント

インストール: npm install vue-simple-uploader --save
使用方法: main.js 内:

'vue-simple-uploader' からアップローダーをインポートします。
Vue.use(アップローダー)

3. vue-simple-uploaderをベースにグローバルアップロードコンポーネントをカプセル化する

vue-simple-uploaderを導入した後、グローバルアップロードコンポーネントglobalUploader.vueをカプセル化します。コードは比較的長いので、全体を公開することはしません。ソースコードは github にあり、ここで順を追って説明します。

テンプレート部分は以下のとおりです。テンプレートとスタイルをカスタマイズしたので、HTML 部分が比較的長くなっています。CSS 部分は今のところ記載していません。独自の UI に合わせて変更できます。主に、 uploaderコンポーネントのoptionsパラメータと、ファイルaddedsuccessprogresserrorのイベントに注目してください。

<テンプレート>
 <div id="グローバルアップローダー">

 <!-- アップロード -->
 <アップローダー
  ref="アップローダー"
  :options="オプション"
  :autoStart="false"
  @ファイルが追加されました="onFileAdded"
  @file-success="onFileSuccess"
  @file-progress="onFileProgress"
  @file-error="onFileError"
  クラス="アップローダーアプリ">
  <アップローダー非サポート></アップローダー非サポート>

  <uploader-btn id="global-uploader-btn" :attrs="attrs" ref="uploadBtn">ファイルを選択</uploader-btn>

  <uploader-list v-show="panelShow">
  <div class="file-panel" slot-scope="props" :class="{'collapse': collapse}">
   <div class="ファイルタイトル">
   <h2>ファイルリスト</h2>
   <div class="操作">
    <el-button @click="fileListShow" type="text" :title="折りたたむ ? '展開':'折りたたむ' ">
    <i class="iconfont" :class="collapse ? 'icon-fullscreen': 'icon-minus-round'"></i>
    </el-button>
    <el-button @click="close" type="text" title="閉じる">
    <i class="iconfont icon-close"></i>
    </el-button>
   </div>
   </div>

   <ul class="ファイルリスト">
   <li v-for="props.fileList 内のファイル" :key="file.id">
    <uploader-file :class="'file_' + file.id" ref="files" :file="file" :list="true"></uploader-file>
   </li>
   <div class="no-file" v-if="!props.fileList.length"><i class="nucfont inuc-empty-file"></i> アップロードするファイルがありません</div>
   </ul>
  </div>
  </アップローダーリスト>

 </アップローダー>

 </div>
</テンプレート>

コンポーネント内のデータ部分:

データ() {
 戻る {
 オプション:
  target: 'http://xxxxx/xx', // アップロード先のURL
  chunkSize: '2048000', // チャンクサイズ fileParameterName: 'file', // ファイルをアップロードするときのファイルパラメータ名、デフォルトファイル
  maxChunkRetries: 3, // 自動で失敗したアップロードの最大再試行回数 testChunks: true, // サーバーの断片化検証を有効にするかどうか // サーバーの断片化検証機能、インスタントアップロードとブレークポイント再開の基礎 checkChunkUploadedByResponse: function (chunk, message) {
  objMessage = JSON.parse(message);
  アップロードをスキップする場合
   true を返します。
  }

  戻り値 (objMessage.uploaded || []).indexOf(chunk.offset + 1) >= 0
  },
  ヘッダー: {
  // ヘッダーに検証が追加されました。実際の業務に応じて設定してください。Authorization: "Bearer " + Ticket.get().access_token
  },
 },
 属性: {
  // 受け入れられるファイルタイプ (['.png'、'.jpg'、'.jpeg'、'.gif'、'.bmp'...] など) ここで accept をカプセル化します: ACCEPT_CONFIG.getAll()
 },
 panelShow: false, //ファイルを選択した後、アップロードパネルを表示します
 }
},

グローバル参照:
app.vueで参照され、常にグローバルコンポーネントとして存在しますが、使用されていないときはアップロードインターフェースは非表示になります。

<グローバルアップローダー></グローバルアップローダー>

4. ファイルアップロードプロセスの概要

1. ボタンをクリックしてファイルのアップロード操作を開始します。

(グローバルアップロード機能を使用せず、直接アップロードをクリックする場合は、この手順を無視してください。)

グローバルアップロードプラグインを作成しているため、まずアップロードウィンドウを非表示にする必要があります。アップロードボタンをクリックすると、Bus を使用してopenUploaderイベントを送信し、 globalUploader.vueでイベントを受信し、 uploader-btnのクリックイベントをトリガーします。

特定のページで、アップロードボタンをクリックし、パラメータ(ある場合)をバックグラウンドに移動します。ここでは、 event busを使用してコンポーネント間で値を渡します。もちろん、 vuexを使用する方が良いでしょう。

バス.$emit('openUploader', {
 上位ID: this.上位ID
})

globalUploader.vueでイベントを受信します:

Bus.$on('openUploader', クエリ => {
 this.params = クエリ || {};

 (this.$refs.uploadBtn)の場合{
	 // ファイル選択ウィンドウが開きます$('#global-uploader-btn').click();
 }
});

2. ファイルを選択すると、アップロードウィンドウが表示され、md5計算が開始されます。

onFileAdded(ファイル) {
 this.panelShow = true;
	
	// 以下で説明する MD5 を計算します this.computeMD5(file);
},

ここには前提があります。 uploaderautoStart falseに設定しました。なぜこれを行うのでしょうか?

ファイルを選択した後、ブレークポイント再開と即時送信の機能を実装するために MD5 を計算する必要があります。したがって、ファイルを選択した後すぐにアップロードを開始することはできません。ファイルのアップロード操作を開始する前に、MD5 の計算が完了するまで待つ必要があります。

具体的なMD5の計算方法については後述しますが、ここでは簡単に紹介します。

アップロードプロセス中、ファイル進行状況のアップロード進行状況コールバックが継続的にトリガーされます。

// ファイル進行状況コールバック onFileProgress(rootFile, file, chunk) {
 console.log(`${file.name} をアップロードしています、チャンク: ${chunk.startByte / 1024 / 1024} ~ ${chunk.endByte / 1024 / 1024}`)
},

3. ファイルが正常にアップロードされた後

ファイルが正常にアップロードされた後、「アップロード完了」コールバックで、サーバーから返されたneedMergeフィールドを使用して、シャードを再度マージする要求を送信する必要があるかどうかが判断されます。
このフィールドが true の場合、マージを要求するためにバックエンドに Ajax リクエストを送信する必要があります。そうでない場合は、アップロードは直接成功します。

注: ここでのneedMerge私とバックエンドによって決定されたフィールド名です

onFileSuccess(ルートファイル、ファイル、レスポンス、チャンク) {
 res = JSON.parse(response); とします。

 // サーバー定義のエラー。アップローダーではインターセプトできません。if (!res.result) {
 this.$message({ メッセージ: res.message、 タイプ: 'error' });
 戻る
 }
	
	// サーバーがマージのリクエストを返す場合 if (res.needMerge) {
 api.mergeSimpleUpload({
  一時名: res.一時名、
  ファイル名: ファイル名、
  ...this.params、
 })。次に、データ => {
  // ファイルのマージが成功しました Bus.$emit('fileSuccess', data);
 }).catch(e => {});
 // マージする必要はありません } else {
 Bus.$emit('fileSuccess', res);
 console.log('アップロードに成功しました');
 }
},

onFileError(ルートファイル、ファイル、レスポンス、チャンク) {
	コンソール.log(エラー)
},

5. ファイルシャーディング

vue-simple-uploaderファイルを自動的に分割し、各部分のサイズはoptionschunkSizeで設定できます。

図に示すように、大きなファイルの場合、複数のリクエストが送信されます。testChunks testChunks trueに設定すると (プラグインのデフォルト設定はtrue )、フラグメント検証のためにサーバーにリクエストが送信されます。以下の最初の get リクエストがこのリクエストであり、後続の各 post リクエストはフラグメントをアップロードするためのリクエストです。

サーバーに送信されたパラメータを確認します。chunkNumber chunkNumber現在のシャード番号を示し、 totalChunksシャードの合計数を表します。両方のパラメータは、設定したchunkSizeに基づいてプラグインによって計算されます。

ファイルのアップロードが成功した場合、バックエンドから返されたフィールドは、バックエンドに別のファイルマージ要求を送信するかどうかを判断するために使用されることに注意してください。

6. MD5の計算プロセス

ブレークポイント再開とインスタント転送の基礎は、ファイルの一意の識別子であるファイルのMD5計算することです。サーバーは、 MD5に基づいてインスタント転送を実行するか、ブレークポイント再開を実行するかを決定します。

file-addedイベントの後、 MD5が計算されます。最終的な目標は、計算されたMD5パラメータに追加してバックエンドに渡し、ファイルのアップロード操作を続行することです。詳細な手順は次のとおりです。

  • アップローダー コンポーネントのautoStart falseに設定します。これは、ファイルを選択した後にアップロードが自動的に開始されないことを意味します。
  • まずfile.pause()でファイルを一時停止し、次にH5のFileReaderインターフェースでファイルを読み取ります。
  • ファイルを非同期に読み取った結果に対してMD5を実行します。ここで使用する暗号化ツールはspark-md5です。 npm install spark-md5 --saveでインストールするか、他の MD5 暗号化ツールを使用することもできます。
  • ファイルには、ファイルの一意の識別子を表すuniqueIdentifierという属性があります。計算された MD5 をこの属性file.uniqueIdentifier = md5に割り当てて、最終的な目的を達成します。
  • file.resume()を介してファイルのアップロードを開始/続行します。
/**
* ブレークポイントの再開と即時転送を実現するために md5 を計算します* @param ファイル
*/
/**
* ブレークポイントの再開と即時転送を実現するために md5 を計算します* @param ファイル
*/
 computeMD5(ファイル) {
 fileReader を新しい FileReader() にします。
 time = new Date().getTime(); とします。
 blobSlice を File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice とします。
 現在のチャンクを 0 にします。
 定数チャンクサイズ = 10 * 1024 * 1000;
 chunks = Math.ceil(file.size / chunkSize); とします。
 spark を新しい SparkMD5.ArrayBuffer() にします。
 
 //ファイルステータスを「MD5 を計算」に設定する
 this.statusSet(ファイルID、'md5');
 
 ファイル.一時停止();
 
 ロードNext();
 
 ファイルリーダー.onload = (e => {
 spark.append(e.target.result);
 (現在のチャンク < チャンク) の場合 {
  現在のチャンク++;
  ロードNext();
  // MD5 計算の進行状況をリアルタイムで表示 this.$nextTick(() => {
  $(`.myStatus_${file.id}`).text('MD5 を検証 '+ ((currentChunk/chunks)*100).toFixed(0)+'%')
  })
 } それ以外 {
  md5 = spark.end(); とします。
  this.computeMD5Success(md5、ファイル);
  console.log(`MD5 計算が完了しました: ${file.name} \nMD5: ${md5} \nセグメント: ${chunks} サイズ: ${file.size} 時間: ${new Date().getTime() - time} ms`);
 }
 });
 fileReader.onerror = 関数(){
 this.error(`ファイル ${file.name} の読み取りエラーです。ファイルを確認してください`)
 ファイル.キャンセル();
 };
 関数loadNext() {
 start = currentChunk * chunkSize とします。
 end = ((start + chunkSize) >= file.size) とします。file.size : start + chunkSize;
 fileReader.readAsArrayBuffer(blobSlice.call(file.file, start, end));
 }
},

computeMD5Success(md5, ファイル) {
 // カスタムパラメータをアップローダーインスタンスのoptsに直接ロードします。Object.assign(this.uploader.opts, {
 クエリ: {
  ...this.params、
 }
 })
 ファイル.uniqueIdentifier = md5;
 ファイル.resume();
 this.statusRemove(ファイル.id);
},

ファイルの uniqueIdentifier プロパティに値を割り当てた後、リクエスト内の識別子は計算された MD5 になります。

7. 即時アップロードと再開ダウンロード

MD5を計算した後、ダウンロードを再開して数秒で転送するという概念について話すことができます。

サーバーは、フロントエンドから送信されたMD5に基づいて、即時送信を実行するか、ブレークポイント再開可能送信を実行するかを決定します。

  • a. サーバーはファイルが正常にアップロードされたことを検出すると、2 番目のアップロード マークを直接返します。
  • b. サーバーは、ファイルが断片的にアップロードされていることを検出すると、断片情報を返し、フロントエンドにアップロードを続行するように指示します。つまり、ブレークポイントからアップロードを再開します。

7.1 フロントエンドの場合

各アップロード プロセスの開始時に、 vue-simple-uploader GET リクエストを送信して、どの部分がアップロードされたかをサーバーに問い合わせます。

このリクエストにはいくつかの結果が考えられます:

a. 2 回目のアップロードの場合は、リクエスト結果に対応するマークが表示されます。たとえば、ここではskipUploadtrueurlが返されていますが、これはサーバーがファイルがすでに存在することを通知していることを意味します。URL url直接提供するので、再度アップロードする必要はありません。これは 2 回目のアップロードです。

図a1: 即時送信の場合のバックエンド戻り値

図a2: GIF送信

b. バックエンドがフラグメント情報を返す場合、これはブレークポイントの再開です。図に示すように、返されたデータにはuploadedフィールドがあり、これはこれらのセグメントがアップロードされていることを意味し、プラグインはこれらのセグメントのアップロードを自動的にスキップします。

図b1: ブレークポイント再開時のバックエンド戻り値

図 b2: ブレークポイント再開 gif

c. 何も返されない場合、これはまったく新しいファイルであり、完全なマルチパートアップロードロジックが実行されます。

7.2 フロントエンドの断片化チェック: checkChunkUploadedByResponse

前回は概念について説明しました。次は、フロントエンドがこれらの戻り値をどのように処理するかについて説明します。
プラグイン自体は、どれをスキップする必要があるかを判断しません。コードでは、 optionscheckChunkUploadedByResponseによって制御されます。XHR 応答コンテンツに基づいて、各チャンクが正常にアップロードされたかどうかを確認します。成功したチャンクは直接スキップされます。この関数で処理し、スキップできる場合は true を返す必要があります。

checkChunkUploadedByResponse: 関数 (チャンク、メッセージ) {
	 objMessage = JSON.parse(message);
 アップロードをスキップする場合
  true を返します。
 }

 戻り値 (objMessage.uploaded || []).indexOf(chunk.offset + 1) >= 0
},

注: skipUploaduploaded 、バックエンドで説明したフィールドです。バックエンドによって実際に返されるフィールド名に従う必要があります。

8. ソースコードと追記

合計でいくつかのファイルがあり、 app.vue 、カプセル化されたグローバルアップロードコンポーネントglobalUploader.vue 、コンポーネントを呼び出すdemo.vueがあり、ソースコードは github にあります: https://github.com/shady-xia/Blog/tree/master/vue-simple-uploader。

globalUploaderソースコード内のticketapiは、独自使用のためのものです。1 つはアクセストークンで、もう 1 つは axios カプセル化に基づくリクエスト ライブラリです。ビジネス ニーズに応じて置き換えてください。また、アップロードインターフェースの展開と折りたたみにはjqueryが使用され、通知には Element コンポーネントが使用されていますが、無視してください。

私のレベルは限られているので、参考までにアイデアを提供しているだけです。

このプラグインをパッケージ化してファイル リソース ライブラリを開発した後、基本的に単純な Baidu Netdisk を実現したことがわかりました。これは、非常に複雑な機能を備えた管理システムであり、詐欺です。

8.1 最初のシャード損失問題について

testChunk を有効にした後にサーバーが最初のフラグメントを受信できない問題について:
simpleUploader のドキュメントには次のように書かれています:

testChunk の get リクエストは、デフォルトで最初のフラグメントをサーバーに送信します。サーバーが 200 ステータスを返す場合、現在のチャンクはアップロードされており、再度アップロードされないものとみなされます。
したがって、ここでサーバーは、200、201、202のセットに含まれないように、204などの他のHTTPステータスコードに変更する必要があります。これは、サーバーにまだこのブロックがないため、標準モードでアップロードする必要があることを意味します。これにより、最初のフラグメントが再度アップロードされます。

2019/8/6更新

1. ファイルMD5の計算方法を最適化し、MD5の計算の進行状況を表示しました。

前回の記事のMD5計算方法は、ファイル全体に対して直接MD5計算する方法でしたが、メモリを大量に消費し、ブラウザがクラッシュしやすかったです。大きなファイルを直接読み込むとメモリ使用量が多すぎて Web ページがフリーズしたりクラッシュしたりすることを防ぐために、ファイルを分割して読み込んでMD5を計算する方法に変更しました。

2. カスタムステータスの追加

(私はこれまでにもいくつかのカスタム状態をカプセル化してきました。最近、何人かの友人から「MD5 の検証」と「マージ」状態がないのはなぜかと尋ねられました。私は独自の方法を書きました。この方法は愚かですが、効果は得られます)

プラグインはもともと、 successerroruploadingpaused 、およびwaitingの状態のみをサポートしています。

ビジネス上のニーズにより、 “校驗MD5”“合并中”“轉碼中”“上傳失敗”

最初のいくつかの状態はすでにプラグインにパッケージ化されているため、ソース コードを変更することはできず、よりハッキーなアプローチしか使用できません。
カスタム状態が開始したら、 statusSetメソッドを手動で呼び出して、元の状態をカバーするpタグを生成する必要があります。また、カスタム状態が終了したら、 statusRemoveを手動で呼び出してタグを削除する必要もあります。

this.statusSet(file.id, 'マージ中');
this.statusRemove(ファイル.id);

具体的な使い方についてはソースコードを参照してください。同時に、simple-uploader のプラグイン作者が将来的にカスタムステータスの設定をサポートすることを期待しています。

これで、ファイルセグメントアップロード、インスタントアップロード、ブレークポイント再開のための vue-simple-uploader カプセル化に基づくグローバルアップロードプラグイン機能に関するこの記事は終了です。vue simple uploader カプセル化に関するその他の関連コンテンツについては、123WORDPRESS.COM で以前の記事を検索するか、次の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • Moment プラグインを使用して時間をフォーマットする vue のサンプルコード
  • vue+element を使用した Google プラグインの開発プロセス全体
  • スワイパープラグインを使用して Vue でカルーセルを実装する例
  • VueにExcelテーブルプラグインを導入する方法
  • Vueカスタムディレクティブを使用してドラッグアンドドロッププラグインを構築する方法
  • Vue コード強調プラグインの総合的な比較と評価
  • vue-cli 3 で vue-bootstrap-datetimepicker 日付プラグインを使用する方法
  • Vue プラグイン エラー: このページで Vue.js が検出されました。問題は解決しました

<<:  MySQL が起動直後にシャットダウンする問題 (ibdata1 ファイルの破損が原因) に対する完璧な解決策

>>:  docker-compose でデプロイしたときに MySQL にアクセスできなくなる問題の簡単な分析

推薦する

JavaScript における BOM と DOM の詳細な説明

目次BOM (ブラウザ オブジェクト モデル) 1. ウィンドウブラウザのウィンドウサイズを取得する...

Linux環境でユーザーにsudo権限を追加する方法

sudo 設定ファイルsudo のデフォルトの設定ファイルは /etc/sudoers です。一般的...

HTML チュートリアル: よく使われる HTML タグのコレクション (5)

関連記事:初心者が学ぶ HTML タグ (4)導入された HTML タグは、必ずしも XHTML 仕...

JS関数の呼び出し、適用、バインドの超詳細な方法

目次JS 関数呼び出し、適用、バインドメソッド1. call() メソッド1. call() メソッ...

MySQL ジョイントインデックス(複合インデックス)の実装

共同インデックスこの記事におけるジョイントインデックスの定義は次のとおりです (MySQL): AL...

Element UI を使用してページにページング ナビゲーション バーを追加する方法

必要ページング バーを追加します。これにより、ページにジャンプしたり、ページ番号に従って特定のページ...

vue の v-for ディレクティブはリストのレンダリングを完了します

目次1. リストの走査2. Vueにおけるキーの役割3. リストフィルタリングこの記事では、Vue ...

myBatis で条件を削除する際のスプライシング問題を解決する

私は今日、mybatis を学び、データベースに対していくつかの簡単な追加、削除、変更、クエリを実行...

優れたウェブサイトのコピーライティングと優れたユーザーエクスペリエンス

ウェブサイトを見るというのは、実は美しい女性を評価するようなものです。見た目を見るとき、私たちは見た...

MySQL 8.x msi バージョンのインストール チュートリアル (画像とテキスト付き)

1. MySQLをダウンロードする公式サイトのダウンロードアドレス https://dev.mys...

Linux コマンドにおける Ctrl+z、Ctrl+c、Ctrl+d の違いと使い方

Linux で Ctrl+c、Ctrl+d、Ctrl+z はどういう意味ですか? Ctrl+c と ...

40 CSS/JSスタイルと機能的な技術処理

1-ドロップダウン選択ボックスのスタイル設定 - ドロップダウン リストを変更します。 2- <...

MYSQLはUnionを使用して2つのテーブルのデータを結合し、表示します。

UNION演算子の使用union : 2 つ以上の SELECT ステートメントの結果を 1 つの...

スライディングカルーセル効果を実現する js

この記事では、スライディングカルーセル効果を実現するためのjsの具体的なコードを参考までに共有します...

MySQL トランザクションの詳細

目次導入取引の4つの特徴トランザクション分離レベル確認するMVCC現在の読書スナップショット読み取り...