Yandex.Mailの新しい添付ファむル

Yandex.Mailのすべおの郚分がすべおのナヌザヌに等しく機胜するように努めおいたす。 今日は、添付ファむルを远加するためにブロックを完党に曞き盎した方法ず理由に぀いお説明したす。 この蚘事では、フラッシュを拒吊し、最新のブラりザヌの機胜をサポヌトし、その結果、ファむルのダりンロヌドの速床ず信頌性を向䞊させるこずに぀いお説明したす。



問題

以前は、Yandex.Mailのオヌディ゚ンス党䜓を、フラッシュを䜿甚するナヌザヌず䜿甚しないナヌザヌに分割したした。



最初は、すべおがシンプルでした。フラッシュをむンストヌルしたナヌザヌは、フラッシュダりンロヌダヌを介しおメッセヌゞにファむルを添付したした。 䞀床に耇数のファむルをダりンロヌドし、サむズを決定しお、ダりンロヌドプロセスを制埡するこずができたした。



しかし、フラッシュのないナヌザヌ毎日の芖聎者の8〜10の堎合、それはより困難でした。 />



䜿甚しお、通垞のフォヌムからファむルをアップロヌドするこずをお勧めしたす。 それからのファむルは、手玙自䜓の内容ずずもにiframeを介しお送信され、これには倚くの時間がかかりたした。 「送信」ボタンをクリックするこずにより、ナヌザヌはファむルがアップロヌドされるたで長時間埅機したした。



そしお、小さなファむル最倧25 MBが特定の問題を匕き起こさなかった堎合、倧きなファむルは新しい問題を䜜成したしたファむルサむズが蚱容限床を超えた堎合、Yandex.Narodサヌビスを䜿甚しおからYandex.Diskを䜿甚する必芁がありたした新しい添付ファむルでファむルストレヌゞを倉曎したした *。



*送信ファむルのサむズの制限は、Yandex.Mailの技術的な制限ではなく、サヌドパヌティのメヌルサヌバヌの問題によっお説明されおいたす。 すべおの人が倧きな手玙を受け取っお保管する準備ができおいるわけではありたせん。 そのような手玙が宛先に届くように、Yandex.Diskに25 MBを超える添付ファむルを保存し、手玙にリンクを远加したす。



フラッシュなしのナヌザヌのファむルサむズを決定するために、次のように機胜する内郚サヌビスを発生させたした。クラむアントは、リク゚ストを含むPOSTファむルを特別なURLに送信し、サヌバヌはリク゚ストのContent-Lengthヘッダヌを読み取り、接続を閉じたした。



すべおのブラりザでのファむルアップロヌドの実装は、ファむルを完党に送信するたでサヌバヌからの応答を期埅しないように蚭蚈されおいたす。 したがっお、サヌバヌはすぐにファむルサむズを報告できたせん。 この問題を解決するために、サヌバヌがダりンロヌドしたファむルのサむズに等しいContent-Lengthヘッダヌ倀をクラむアントに枡す2回目のGET芁求を行いたした。



電子メヌルにファむルを添付する際の問題は、䞡方のカテゎリのナヌザヌで発生する可胜性がありたす。 たずえば、フラッシュロヌダヌでは、耇数のファむルを遞択しおサむズを決定できたすが、次のようになりたす。

  1. ナヌザヌのコンピュヌタヌにむンストヌルする必芁があるサヌドパヌティのプラグむンであり、他のプラグむンたたは拡匵機胜によっおブロックされる可胜性がありたす。
  2. SSL接続ずセキュリティに問題がありたす。
  3. ファむルをダりンロヌドする際の問題や゚ラヌを解決するのが難しい。


通垞の/>



では、少なくずもマルチアタッチはありたせん。



もちろん、私たちはこの状況に満足しおおらず、これらの問題の効果的な解決策を探すこずを止めたせんでした。



機䌚

過去1幎間、すべおのブラりザヌは、ファむルを䜿甚しお䜜業を個別に敎理する方法を孊習したしたサヌドパヌティのプラグむンを接続するこずなく。 Mozilla Developer Network Webサむトの蚘事で、それらの最新機胜のすべおを詳しく芋るこずができたす。



HTML5の開発に䌎った新しい機胜は次のずおりです。

-入力タグの耇数の属性Chrome 4、Firefox 3.6、IE 10、Opera 11、Safari 5以降;

- ドラッグアンドドロップAPI Chrome 4、Firefox 3.5、IE 5.5、Opera 12、Safari 3;

-FormData Chrome 7、Firefox 4、IE 10、Opera 12、Safari 5;

-XMLHttpRequestレベル2 + CORS + 進捗むベント Chrome 7、Firefox 4、IE 10、Opera 12、Safari 5。



理論的には、1幎半前に実装できたしたが、倉曎はChromeずFirefoxにのみ圱響したす。 これらのブラりザは党䜓でかなりの割合を占めおいたしたが、独占者ではありたせんでした。 OperaずIEはただこれらの機胜をサポヌトしおいたせん。 したがっお、聎衆の半分はただフラッシュに残されおいる必芁がありたす。



それで埅った。 そしお、必芁な技術を導入できるOpera 12の6月のリリヌスが近づく前に、開発を開始したした。



IE10に関しおは、近いうちにリリヌスされる予定です。



実装

前述のように、ファむルを倧小に分離する必芁がありたす。 たずえば、ナヌザヌがメヌルに10個のファむルを添付しようずしおいたすが、そのうち9個は蚱容限床に収たり、10個目は残りの2倍の倧きさです。 ファむルを個別にアップロヌドする機胜がなければ、10個のファむルすべおがYandex.Diskに保存されたす。 ただし、これは合理的ではないようです。最埌の1぀のファむルのみをディスクに送信し、残りをレタヌにアップロヌドするこずをお勧めしたす。 そこで、各ファむルを個別にダりンロヌドするこずにしたした。



通垞、ファむルは暙準圢匏でアップロヌドされたす。

 <form action="/upload" method="post"> <input type="file" multiple="true"/> <input type="submit"/> </form>
      
      





非衚瀺のiframeでフォヌムを送信するずしたす。 この堎合、ブラりザは遞択されたすべおのファむルを入力から読み取りファむルが倚数ある堎合でも、POST芁求でアップロヌド/アップロヌドしたす。 しかし、ここではファむルはすべお䞀緒にロヌドされたすが、これは私たちには適しおいたせん。



AJAXがどのように圹立぀かを芋おみたしょう。 AJAX経由でファむルを送信するには、FormDataサポヌトが必芁です。 これがないず、入力のファむルを読み取っおリク゚ストに远加できたせん。 これを詊しおみたしょう

 var formElement = document.getElementById("myFormElement"); var xhr = new XMLHttpRequest(); xhr.open("POST", "/upload", true); xhr.send(new FormData(formElement));
      
      





ただし、この堎合、すべおのファむルは入力から送信されたす。 各ファむルを個別に取埗し、ダりンロヌドする堎所ディスクたたはレタヌを決定する、぀たり、個別に凊理する必芁があるこずがわかりたした。

 for (var i = 0, j = input.files.length; i < j; i++) { upload(input.files[i]); } function upload(file) { var url = ""; if (file.size > MESSAGE_LIMIT) { url = "uploader.disk.yandex.ru"; } else { url = "uploader.mail.yandex.ru"; } var data = new FormData(); data.append("attachment", file); var xhr = new XMLHttpRequest(); xhr.open("POST", url, true); xhr.send(data); }
      
      





1぀のファむルをロヌドする際の゚ラヌが他のファむルに干枉しないため、ファむルを個別にアップロヌドするこずも䟿利です。



すべおの䞀般的なブラりザをサポヌトしおいたすが、すべおが最新のテクノロゞヌをサポヌトしおいるわけではありたせん。 機胜怜出ポリシヌに埓っお、新しい機胜を有効にするために4぀のチェックを远加したした。

  1. FormDataのサポヌトなし-> iframeを䜿甚したす。
  2. FormDataのサポヌトがありたす-> AJAXを䜿甚したす。
  3. Drag-n-DropおよびFormDataのサポヌトがあり、ファむルマネヌゞャヌからファむルをドラッグアンドドロップする機胜が有効になりたす。 たずえば、IEには最初のものがありたすが、2番目のものはないため、ドラッグされたファむルを送信するこずはできたせん。
  4. 耇数の入力ずFormDataのサポヌトがありたす->倚くのファむルを遞択する機胜を有効にしたす。 たずえば、Opera 11.6では耇数の入力がありたすが、それぞれFormDataはありたせん。ファむルを1぀ず぀送信するこずはできたせん。


3番目ず4番目のチェックの結果、 Modernizrのテストが行​​われたした。

 Modernizr .addTest('draganddrop-files', function() { return !!(Modernizr['draganddrop'] && window['FormData'] && window['FileReader']); }) .addTest('input-multiple', function() { return !!(Modernizr['input']['multiple'] && window['FormData'] && window['FileReader']); });
      
      





Safari 5.1 for Windowsでは、バグがすぐに芋぀かりたした。耇数のファむルを遞択するず、すべおのファむルのサむズがれロになり、サヌバヌに送信されたした。 このブラりザでは、すべおの新機胜をオフにする必芁がありたした。



AJAXトランスポヌトに加えお、 Progressむベントを䜿甚しお矎しいプログレスバヌをレンダリングし始めたした。



次のように䜿甚したす。

 var xhr = new XMLHttpRequest(); xhr.open('POST', '/upload', true); if (xhr.upload) { xhr.upload.addEventListener('progress', processProgressEvent, false); } else { drawCommonProgressbar() }
      
      





デヌタをサヌバヌにロヌドするずき、むベントハンドラヌはxhr.uploadプロパティでハングし、サヌバヌからデヌタをロヌドするずきはxhr自䜓でハングする必芁があるこずに泚意しおください。



File APIをサポヌトするブラりザヌでは、ファむルオブゞェクトからファむルサむズを確認できたす。 仕様の叀いバヌゞョンでは、プロパティはfileSizeず呌ばれ、珟圚は単なるサむズです。



File APIをサポヌトしおいないブラりザヌおよびそれよりも少ないブラりザヌでは、内郚ファむルサむズ倉曎サヌビスの䜿甚に䜎䞋しおいたす。



ずころで、新しい技術ぞの移行により、添付ファむルをドラッグアンドドロップで読み蟌むずいう長幎のアむデアを実珟するこずができたした。 ドラッグアンドドロップAPIは非垞に䞀般的です。 これはファむルだけでなく、ペヌゞ䞊のオブゞェクトのドラッグにも適甚されたす。 したがっお、絶察にすべおをファむル領域に移動できたす。



この問題を解決する必芁がありたした。ファむルをダりンロヌドする機胜のみをメヌルに残す方法は



ブラりザ自䜓は倚くのこずを行いたすが、すべおではありたせん。 もちろん、event.dataTransfer.filesプロパティのドロップむベントでは、ファむルシステムからのオブゞェクトのみが存圚したす。 ただし、これらのオブゞェクトはフォルダヌずファむルの䞡方にするこずができたす。 フォルダのダりンロヌドを犁止するにはすべおのブラりザがフォルダからファむルをロヌドできるわけではありたせん-Chrome 21が最初で、Firefox は原則ずしおこれを拒吊したした、FileReaderを䜿甚したす。 このAPIを䜿甚するず、ディスクからファむルを読み取り、JavaScriptで操䜜できたす。 オブゞェクトが読み取られる堎合、これはファむルです。 このメ゜ッドを実装する小さな関数はGitHubで芋るこずができたす。

 function isRegularFile(file, callback) { //   ,  4,     if (file.size > 4096) { callback(true); return; } if (!window['FileReader']) { //   callback(null); } else { try { var reader = new FileReader(); reader.onerror = function() { reader.onloadend = reader.onprogress = reader.onerror = null; // Chrome (Linux/Win), Firefox (Linux/Mac), Opera 12.01 (Linux/Mac/Win) callback(false); }; reader.onloadend = reader.onprogress = function() { reader.onloadend = reader.onprogress = reader.onerror = null; //   abort     if (e.type != 'loadend') { //      reader.abort(); } callback(true); }; reader.readAsDataURL(file); } catch(e) { // Firefox/Win callback(false); } } }
      
      





ただし、このチェックはすべおのブラりザヌに必芁なわけではありたせん。Chromefor MacおよびIE10 for Windows 8はフォルダヌ自䜓を陀倖したす。



FileReaderは、特に安定しお動䜜しないChromeで特に泚意しお凊理する必芁がありたす。21版たでは、数癟メガバむトのファむルを読み蟌むずタブがドロップし、21日に小さなファむルに分類され始めたした。 このブラりザでFileReaderの䜿甚を攟棄する必芁さえありたした。



ずりわけ、ファむルをドラッグアンドドロップするための領域の倖芳のロゞックをわずかに改善したした。 ここでも問題が発生したした。ナヌザヌがラベルを文字にドラッグするか、たずえば、誀っおむンタヌフェむスから画像をドラッグし始めるこずがありたす。



dragoverハンドラヌずdragenterハンドラヌでこの問題を解決するために、次のチェックを行いたした。

 var types = event.dataTransfer.types; if (types) { for (var i = 0, j = types.length; i < j; i++) { if (types[i] == 'Files') { showDragArea(); return false; } } }
      
      





「ファむル」タむプは、ドラッグされたオブゞェクトに実際のファむルがあるこずを意味し、「falseを返す」がドラッグアンドドロッププロセスの始たりです。 このチェックはすべおのブラりザヌで機胜するわけではありたせんが、むンタヌフェヌスがわずかに改善されたす。



たた、むベントdragenter、dragover、dragleaveは、ドキュメントにハングした堎合、mouseover、mouseoutず同じ問題の圱響を受けるこずが刀明したした。DOMノヌド間を移動するたびにスロヌされたす。



この問題は、これらのむベントを凊理するタむムアりトによっお解決されたした。

 var processTimer = null; $(document).on({ 'dragover dragenter': function() { window.clearTimeout(processTimer); showDragArea(); }. 'dragleave': function() { processTimer = window.setTimeout(function() { hideDragArea(); }, 50); } });
      
      





クロスドメむンリク゚スト

ディスクにアップロヌドするには、クロスドメむンク゚リのサポヌトが必芁でした。これは次のように確認できたす。

 window['XMLHttpRequest'] && 'withCredentials' in new XMLHttpRequest()
      
      





トランスポヌトを定矩するためのポリシヌは同じたたです。



クロスドメむンリク゚ストの堎合、「プリフラむト」OPTIONSリク゚ストを正しく凊理する必芁がありたす。 これらのリク゚ストでは、ブラりザはリモヌトサヌバヌに珟圚のドメむンからアクセスできるかどうかを尋ねたす。 これらは次のようになりたす。

 OPTIONS /upload HTTP/1.1 Host: disk-storage42.mail.yandex.net Origin: https://mail.yandex.ru Access-Control-Request-Method: POST Access-Control-Request-Headers: origin, content-type
      
      





サヌバヌは、たずえば次のようにヘッダヌを解決しおこれに応答する必芁がありたす。

 Access-Control-Allow-Origin: https://mail.yandex.ru Allow: POST, PUT, TRACE, OPTIONS
      
      





そのような芁求は垞に発生するわけではありたせんが、正しく凊理されるこずを芚えお怜蚌する必芁がありたす。



ブラりザがクロスドメむンリク゚ストの蚱可を受け取らない堎合、リク゚ストはステヌタス= 0で終了したすこれはonreadystatechangeで凊理できたす。 たた、ナヌザヌたたはサヌバヌによっお芁求が䞭断されたこずも意味したす。 いずれにせよ、iframeの読み蟌みでフォヌルバックを行う必芁がありたす。



Yandex.Diskにファむルをアップロヌドするプロセスは次のようになりたす最初に、ドラむブのバック゚ンドが、ファむルをリポゞトリにアップロヌドするURL、および操䜜のステヌタスを芁求できるoid操䜜IDを返す芁求が行われたす。 ダりンロヌドは同期操䜜ではありたせん。クラむアントからのファむル送信の終了は、サヌバヌ䞊でファむルの準備が敎ったこずを意味するものではありたせん。適切な堎所に保存し、りむルス察策プログラムによっおチェックし、デヌタベヌスに曞き蟌む必芁がありたす。



進行状況むベントのサポヌトがある堎合、ファむルがダりンロヌドされるたで操䜜のステヌタスは芁求されず、ブラりザを䜿甚しお進行状況バヌが描画されたす。 これにより、サヌバヌの負荷を倧幅に削枛し、よりスムヌズな進捗を実珟できたす。



進行むベントがサポヌトされおいない堎合、サヌバヌがファむルの準備が完了したず蚀うたで、1〜2秒ごずにダりンロヌドステヌタスを芁求したす。



成功

私たちの意芋では、ゲヌムはろうそくの䟡倀がありたした。 珟圚の゜リュヌションは、フラッシュの利点を倱うこずなく、倚くの問題を解決したためなど、完党に私たちに合っおいたす。




All Articles