HTML5 – ファイルアップロード プログレスバー付

XPのサポート終了をきっかけに、ブラウザも一斉にIE11が主流になるという大変な変化が起こっています。IE11はWindows8のタッチパネル用のタッチイベントという独自の規格を装備していて、マウス操作がうまく動かないという現象が起きていました。その修正をしていたついでに、これが主流になるなら、HTML5とAjaxを使用した、複数ファイルアップロードを作ることにしました。jQueryを使用しない自前のAjaxでできてるので、jQueryのバージョンアップやIE対応を待つ必要がなく、自分で拡張したり、応用することができます。プログレスバーはその応用で作りました。ちなみに、jQueryで全て作っている人は、前述のIE11のタッチイベントの問題は、自力では解決できません。

複数ファイルの選択ができますか?お手持ちのブラウザで試してください。

対応のブラウザ
モダンブラウザのie11、chrome、safari5でOKです。safariは以前(4.0)から複数のファイルを選択できましたが、そのファイルをjavascriptで扱えない場合があります。最新バージョンのなら大丈夫です。
・Safari
windows版のsafariは5.1.7までありますが、全て動作しません。macのsafariは5.0から正常に動作することが分かりました。safari4.0は複数選択はできるが、JavaScriptでエラーを起こします。したがって、バージョンで判断して分岐させる予定です。

不便だったファイル送信
HTMLでのファイル送信は、大変不便なものでした。選べるのは1ファイルだけ、しかも選んだファイルの操作は一切できず、だだブラウザまかせでボタンを押すだけという、とてもアプリとはいえない粗末なインターフェースでした。そのため多くのWeb開発者は、Flashで開発されたアップローダーを使用したり、独自にアップローダー開発したりしてファイル送信のインターフェースを開発していました。GoogleやWordPressもFlashアップローダーを使用していました。
このファイル操作の不便さは、HTMLやjavascriptでPC内をアクセスできないようにするという意味があるようです。簡単に公開するとjavascriptでPCの個人情報を皆持っていかれてしまうという危険があるわけです。インターネット(HTML)は便利さと危険性のせめぎ合いなわけです。
その理由からか、初期のiPadは1ファイルのファイル送信もグレーアウトで使用できなくしてありました。Flashのアップローダーはもちろん使えません。これでは画像とかのアップロードはどうするんだという感じでした。

ようやく使えるmultiple属性

<input type="file" name="fff" multiple size="20">

↑上記の複数ファイル選択のタグです。
HTML5で追加されたmultiple属性は、ChromeやSafariでは以前から対応していましたが、IE(IE9)では使えませんでした。IE11でやっと使えるようになりました。IE11は前述のタッチイベントの問題がありますが、XPがなくなれば、これを使うしかしかありません。HTML開発者には悪評高いIEも、ようやくHTML5に対応できたといえます。このタグをフォームに貼って送信すれば、複数のファイルがサーバーに送られます。

Ajaxファイルアプリケーション
このファイル情報をAjax(Javascript)で扱うことにより、より複雑で、ユーザーインターフェースのよいファイルアプリケーションを作ることができます。そのためにHTML5ではJavascriptでファイルとフォームを扱えるようにするオブジェクトが追加されています。それが以下のFileApiとFormDataです。
ここでは、このブラウザがFileApiとFormDataが使えるかどうかをチェックしています。その情報をサーバーに送ります。次にフォームで入力された複数のファイルデータを、別に用意した作業用のフォームに一個づつコピーします。作業用のフォームにコピーしたファイルをAjaxでサーバーに送るという仕組みで、それを選択されたファイル数だけ繰り返します。送信する前にこれも新しく追加されたupload.progressイベントを設定しています。
これまでのHTML、Javascriptでは実現できなかったアプリケーションがこれで可能になります。Googleを始めとするクラウド化の企業やjQueryプログラマー達の意思の結果だと思います。

//Fileと.FormDataが使用できるか調べる
window.onload = function(){
  if (window.File && window.FormData){
    document.fm01.fapi.value = 1 ;
  }
}

//別フォームへ指定の1ファイルをコピー
var fm = document.fmwk;
var fdata = new FormData(fm);
var fw = document.form1.fs.files[ban-1];
fdata.append("afile",fw);

//Ajaxでフォームを送信 (※概要です)
var req = new XMLHttpRequest();
//プログレス用のイベントハンドラを設定
req.upload.addEventListener('progress',progress_s,false);
//コールバック関数を設定
req.onreadystatechange = function(){ 
    resp_s(mode,ban,req);
}
//aurl=受信URL
req.open("POST",aurl,true);
req.send(fdata);

プログレスバーを作成
プログレスハンドラには、アップロードの総量と処理量が送られてきます。それをパーセントに計算して、ボックスの幅に設定してやります。これでプログレスバーが動きます。

 

//プログレス ハンドラ
function progress_s(evt) {
  if (evt.lengthComputable) {
    var lsu = evt.loaded;
    var tsu = evt.total;
    var per = Math.round(lsu * 100 / tsu);
    document.getElementById('progress').style.width = 
      per + "%";
  }
}

プログレスバーボックスモデル
<div style="width:270px;height:10px;
  background:#dcdcdc;">
<div id="progress" style="width:150px;height:10px;
  background-color:#8FC31F;font-size:2px">
</div>
</div>

プログレスバー全ファイル
実際には、プログレスハンドラには、ブラウザへの読み込み量が送られてくるようで、送信済みとはタイムラグが発生し、スムーズに動きません。そのため、ここでは全体のファイル数、処理済みのファイル数を同時に計算して、プログレスハンドラでは0.75の処理としています。残りは、コールバック関数内で、1ファイル分を100%処理済み(終了)としてやります。これで、全体のプログレスバーがスムーズに動きます。

//プログレス ハンドラ
//f_ban filesu はグローバル
function progress_s(evt) {
 if (evt.lengthComputable) {
  var bun = evt.loaded / evt.total;
  var fsumi = (f_ban - 1) / filesu;
  var fbun = 1 / filesu * 0.75 ;
  var fper = Math.round((fsumi + (fbun * bun)) * 100);
  document.getElementById('progressf').style.width = 
   fper + "%";
 }
}
//コールバック関数(1ファイル終了)
function refs_s(ban,rtxt){
 ……
 ……
  var per = Math.round(ban * 100 / filesu);
 document.getElementById('progressf').style.width = 
  per + "%";
}

ドラッグ&ドロップは延期
サーバー側のアプリは、従来の1ファイル受信用のものが流用できますから、phpでもaspでも自由に開発できると思います。
HTML5では、ファイルのドラッグ&ドロップのインターフェースも追加されています。モダンなスタイルでいいという人もいますが、ファイル送信には間違い易いという人もいます。クラウドは若い人達だけが使うものではありません。やはり間違いなく使えるというのが一番大事なことだと思います。したがって今回はドラッグ&ドロップの開発は延期することにしました。いずれまた……。

Comments are closed.