最近、非常に大きなファイルをアップロードする必要が出てきたので、QiniuとTencent Cloudのスライスとマルチパートのアップロード機能を調べたので、ここにフロントエンドの大きなファイルのアップロードに関連する機能の実装を示します。
一部のビジネスでは、ライブラリへの大きなExcelテーブルデータのインポート、ビデオファイルのアップロードなど、大きなファイルのアップロードがより重要なインタラクティブシナリオです。ファイルサイズが比較的大きい場合、またはネットワークの状態が悪い場合、アップロード時間が長くなり(より多くのメッセージを送信する必要があり、パケット損失と再送信の可能性が高くなります)、ユーザーはページを更新できず、要求が完了するまで辛抱強く待つだけです。 。
以下は、ファイルアップロードメソッドから始まり、大きなファイルをアップロードするためのアイデアを整理し、関連するサンプルコードを示します。PHPには便利なファイル分割およびスプライシングメソッドが組み込まれているため、サーバーサイドコードはサンプル書き込みにPHPを使用します。
この記事の関連サンプルコードは、主要なリファレンスであるgithubにあります。
大きなファイルのアップロードについて話す
大きなファイルの切り取りアップロード
ファイルをアップロードするいくつかの方法
最初に、ファイルをアップロードするいくつかの方法を見てみましょう。
通常のフォームのアップロード
PHPを使用して通常のフォームのアップロードを表示することは良い選択です。最初にファイルアップロードフォームを作成し、フォームの送信されたコンテンツタイプをenctype = "multipart / form-data"として指定します。これは、フォームがバイナリデータをアップロードする必要があることを示します。
次に、index.phpアップロードファイル受信コードを記述し、move_uploaded_fileメソッドを使用します(php Dafaが最適です...)
フォームフォームで大きなファイルをアップロードすると、サーバーのタイムアウトの問題が発生しやすくなります。xhrを介して、フロントエンドは非同期のファイルアップロード操作を実行することもできます。通常、2つのアイデアがあります。
ファイルエンコーディングのアップロード
最初のアイデアは、ファイルをエンコードしてからサーバーでデコードすることです。フロントエンドで画像の圧縮とアップロードを実現するブログを書きました。主な実装原理は、画像をbase64に変換して送信することです。
varimgURL = URL.createObjectURL(file);
ctx.drawImage(imgURL、0、0);
//画像のエンコーディングを取得し、画像を長い文字列として渡します
vardata = canvas.toDataURL( "image / jpeg"、0.5);
サーバー側で行う必要があることも比較的簡単で、最初にbase64をデコードしてから、画像を保存します。
$ imgData = $ _REQUEST ['imgData'];
$ base64 = explode( '、'、$ imgData)[1];
$ img = base64_decode($ base64);
$ url = './test.jpg';
if(file_put_contents($ url、$ img)){
exit(json_encode(array(
url => $ url
)));
}
base64エンコードの欠点は、ボリュームが元の画像よりも大きいことです(Base64は3バイトを4バイトに変換するため、エンコードされたテキストは元のテキストよりも約3分の1大きくなります)。ファイルの場合、アップロードと解析の時間が大幅に増加します。
base64の詳細については、Base64のメモを参照してください。
base64エンコーディングに加えて、フロントエンドでファイルコンテンツを直接読み取って、バイナリ形式でアップロードすることもできます。
//バイナリファイルを読み取ります
functionreadBinary(text){
vardata = newArrayBuffer(text.length);
varui8a = newUint8Array(データ、0);
for(vari = 0; i <text.length; i ++){
ui8a [i] =(text.charCodeAt(i)&0xff);
}
console.log(ui8a)
}
varreader = newFileReader;
読者。=関数{
readBinary(this.result)//結果を読み取るか、直接アップロードします
}
//入力から読み取ったファイルの内容をfileReaderの結果フィールドに入れます
reader.readAsBinaryString(file);
formData非同期アップロード
FormDataオブジェクトは主に、Ajaxリクエストをより柔軟に送信できる、リクエストを送信するためのキーと値のペアのセットをアセンブルするために使用されます。FormDataを使用してフォーム送信をシミュレートできます。
letfiles = e.target.files //入力ファイルオブジェクトを取得する
letformData = newFormData;
formData.append( 'file'、file);
axios.post(url、formData);
サーバーの処理方法は、基本的に直接フォーム要求と同じです。
iframeがページを更新しない
低バージョンのブラウザー(IEなど)では、xhrはformdataの直接アップロードをサポートしていないため、フォームを使用してファイルをアップロードすることしかできず、フォーム送信自体がページにジャンプします。これは、フォームフォームのtarget属性が原因です。 、その値は
デフォルト値の_selfは、同じウィンドウで応答ページを開きます。
_blank、新しいウィンドウで開く
_parent、親ウィンドウで開く
_top、一番上のウィンドウで開く
フレーム名、指定した名前でiframeで開く
ファイルを非同期でアップロードする感覚をユーザーに体験させる必要がある場合は、フレーム名でiframeを指定できます。フォームのターゲット属性を非表示のiframeに設定すると、返されたデータはこのiframeによって受け入れられるため、iframeのみが更新されます。返された結果については、このiframe内のテキストを解析して取得することもできます。
functionupload {
varnow = + newDate
varid = 'frame' + now
$( "body")。append( `<iframe style =" display:none; "name =" $ {id} "id =" $ {id} "/>`);
var $ form = $( "#myForm")
$ form.attr({
"アクション": '/index.php'、
"メソッド": "投稿"、
"enctype": "multipart / form-data"、
"エンコーディング": "マルチパート/フォームデータ"、
「ターゲット」:id
})。参加する
$( "#" + id).on( "load"、function {
varcontent = $(this).contents.find( "body")。text
試す{
vardata = JSON.parse(content)
} catch(e){
console.log(e)
}
})
}
大きなファイルのアップロード
次に、上記のいくつかのアップロード方法で大きなファイルをアップロードするときに発生するタイムアウトの問題を見てみましょう。
フォームアップロードと更新ページなしのiframeアップロードは、実際にはフォームタグを介してファイルをアップロードしています。このようにして、リクエスト全体がブラウザに完全に渡されて処理されます。大きなファイルをアップロードすると、リクエストがタイムアウトする場合があります。
fromDataを介して、実際には一連のリクエストパラメータをxhrにカプセル化してフォームリクエストをシミュレートします。これにより、大きなファイルのアップロードタイムアウトの問題を回避できません。
アップロードをエンコードすると、アップロードされたコンテンツをより柔軟に制御できます
大きなファイルをアップロードする際の主な問題は、同じリクエストで大量のデータをアップロードする必要があるため、比較的長いプロセスが発生し、失敗した後にアップロードを再開する必要があることです。想像してみてください。このリクエストを複数のリクエストに分割すると、各リクエストの時間が短縮され、リクエストが失敗した場合は、最初からやり直すことなくこのリクエストを再送信するだけで済みます。これにより、大きなファイルを解決できますアップロードの問題はどうですか?
上記の問題に基づいて、大きなファイルのアップロードは次の要件を満たす必要があるようです
分割アップロードリクエスト(スライス)のサポート
ブレークポイント再開をサポート
表示アップロードの進行状況をサポートし、アップロードを一時停止します
次に、これらの関数を順に実装してみましょう。最も重要な関数はスライスである必要があります。
ファイルスライス
参照:大きなファイルのカットとアップロード
エンコーディングアップロードでは、ファイルのバイナリコンテンツをフロントエンドで取得し、コンテンツを分割して、最後に各スライスをサーバーにアップロードするだけです。
Javaでは、ファイルファイルオブジェクトはBlobオブジェクトのサブクラスです。Blobオブジェクトには重要なメソッドスライスが含まれています。このメソッドを使用して、バイナリファイルを分割できます。
以下はファイルの分割の例です。up6の場合、開発者は分割の詳細を気にする必要はありませんが、コントロールはそれを実現するのに役立ちます。開発者はビジネスロジックのみを気にする必要があります。
コントロールがアップロードされると、関連する情報が各ファイルブロックデータに追加され、開発者はサーバーでデータを受信した後にそれを処理できます。
サーバーがこれらのスライスを受信した後、それらを一緒にスプライスすることができます。以下は、PHPスプライシングスライスのサンプルコードです
up6の場合、開発者はスプライシングを行う必要がありません。up6はサンプルコードを提供し、このロジックを実装しています。
一意性を確保するために、コントロールはブロックインデックス、ブロックMD5、ファイルMD5などの情報を各ファイルブロックに追加します
http
Up6には独自の再開機能があり、サーバー側にファイル情報、クライアント側にファイル進捗情報を保存しています。コントロールはアップロード中にファイルの進行状況情報を自動的にロードし、開発者はこれらの詳細を気にする必要はありません。ファイルブロックの処理ロジックでは、ファイルブロックインデックスに従って識別する必要があります。
このとき、アップロード時にページを更新するか、ブラウザを閉じて、同じファイルを再度アップロードしてください。以前にアップロードしたスライスは、再度アップロードされません。
サーバーが再開可能なアップロードを実現するロジックは基本的に似ていますが、サーバーのクエリインターフェースがgetUploadSliceRecord内で呼び出されてアップロードされたスライスのレコードを取得する限り、ここでは展開されません。
さらに、再開可能な転送では、スライスの有効期限を考慮する必要があります。mkfileインターフェースが呼び出された場合、ディスク上のスライスの内容を消去できます。クライアントがmkfileインターフェースを呼び出していない場合、これらのスライスをディスクに保存したままにしておくのは明らかです。信頼性が低い。通常の状況では、スライスのアップロードには有効期間があり、有効期間が経過すると、それらは消去されます。上記の理由により、再開可能な送信では、スライスの有効期限の実装ロジックも同期する必要があります。
再開効果
進行状況をアップロードして一時停止
xhr.uploadのprogressメソッドを使用して、各スライスのアップロードの進行状況を監視できます。
アップロード一時停止の実現も比較的簡単です。xhr.abortを使用すると、現在未完了のアップロード済みスライスのアップロードをキャンセルして、アップロード一時停止の効果を得ることができます。アップロードの再開は、ブレークポイントの再開に似ています。まず、アップロードされたスライスリストを取得してから、未完了のアップロードを再送信します。アップロードされたスライス。
スペースが原因で、アップロードの進行状況と一時停止の機能はここでは実装されません。
実現効果:
概要
現在、Qiniu SDK、Tencent Cloud SDKなど、成熟した大きなファイルのアップロードソリューションがコミュニティにいくつかあります。単純な大きなファイルのアップロードライブラリを手動で実装する必要はないかもしれませんが、その原理を理解する必要があります。
この記事では、最初にフロントエンドファイルをアップロードするいくつかの方法を整理してから、大きなファイルをアップロードするためのいくつかのシナリオと、大きなファイルをアップロードするために実装する必要があるいくつかの機能について説明します
Blobオブジェクトのスライスメソッドを使用してファイルをスライスに分割する
サーバーがファイルを復元するために必要な条件とパラメーターを整理し、PHPがスライスをファイルに復元することを示した
アップロードされたスライスのレコードを保存することによる再開可能なアップロード
ファイルのマージ時のメモリオーバーフローの回避、無効化戦略のスライス、アップロードの進行の一時停止など、詳細に実装されていない機能や、1つずつ実装されていないその他の機能など、まだいくつかの問題が残っています。
ほとんどのバックエンドコードロジックは同じで、現在MySQL、Oracle、SQLをサポートしています。使用する前にデータベースを構成する必要があります。私が書いたこの記事を参照できます。http ://blog.ncmem.com/wordpress/2019/08/12/java-http%E5%A4%A7%E6%96%87 %E4%BB%B6%E6%96%AD%E7%82%B9%E7%BB%AD%E4%BC%A0%E4%B8%8A%E4%BC%A0 /
一緒に議論するグループへようこそ:374992201