今風のWebサービスを使っていると重たい処理などを行う際に画面上にプログレスバーが表示されたりして、ユーザーのイライラを緩和する措置が取られたりしています。
特に重たいファイルをアップロードする場合などにプログレスバーが表示されれば進捗もわかり、ユーザー的にも後どれぐらい待てばいいのかといった状況が把握しやすくなります。
今回は、PHPのキャッシュモジュールとして使われているAPC とjQueryUIのProgressbarウィジット を使って、ファイルアップロード時にプログレスバーを表示するというプログラムを書いてみたいと思います。
プログレスバーのイメージとAPCの環境設定
今回実装するプログレスバーのイメージは下記のようなものです。
進捗率の表示などはオリジナルな部分はありますが、ファイルを指定してからアップロード完了までの間、画面上にプログレスバーが伸びていき状況が把握できるようになっています。
通信は非同期に行われるため画面遷移が無く、ファイルアップロード画面だけで処理が完結できるようになっています。
今回の実行環境はPHP5.3.3、APCは3.1.13を使っています。
APC導入に関して詳細は書きませんが、PHPの設定ファイル(php.ini)に下記の設定項目を追加しておきます。
apc.rfc1867 = On
これにより、ファイルアップロードの進捗ハンドラが有効になります。
詳細は、マニュアル にも記載があるので確認するとよいかもしれません。
ファイルアップロード時にプログレスバーを表示するプログラム
今回作成したプログラムは、3つに分かれています。
1つ目がファイルアップロード画面のプログラム、2つ目が送られたファイルのチェックやアップロード後の処理を行うプログラム、3つ目が進捗管理用のプログラムです。
順番に見て行きますが、1つ目のファイルアップロード画面(index.php)は下記のようになっています。
<html>
<head>
<meta http-equiv='Content-Type' content='text/html;charset=utf-8'>
<link rel='stylesheet' href='//code.jquery.com/ui/1.10.4/themes/smoothness/jquery-ui.css'>
<script src='//code.jquery.com/jquery-1.10.2.js'></script>
<script src='//code.jquery.com/ui/1.10.4/jquery-ui.js'></script>
<style type='text/css'>
.upload {
padding:10px;
}
#progressbar {
position: absolute;
margin: 10px;
width:500px;
}
#loading {
position: absolute;
left: 50%;
}
</style>
<script>
var timer = null;
var progressMeter = 0;
// ファイルアップロード
function formSubmit() {
$('#progressbar').progressbar({
value: false,
change: function () {
$('#loading').text($('#progressbar').progressbar('value') + '%');
}
});
var formData = new FormData();
formData.append('APC_UPLOAD_PROGRESS', $('#progress_key').val());
if ($('#uploadFile').val() !== '' ) {
formData.append('uploadFile', $('#uploadFile').prop('files')[0] );
}
timer = setInterval('progressBar()', 3000);
$.ajax('/upload.php', {
method: 'POST',
contentType: false,
processData: false,
data: formData,
dataType: 'text',
error: function (XMLHttpRequest, errorText) {
clearInterval(timer);
console.log(errorText);
},
success: function (res) {
clearInterval(timer);
if (res) {
// Uploadしたファイルのエラー内容を受信・出力
alert(res);
$('#progressbar').progressbar("destroy");
} else {
$('#progressbar').progressbar('value', 100);
location.href = 'index.php';
}
}
});
}
// 進捗割合の取得・表示
var progressBar = function () {
jqxhr = $.ajax('/progress.php', {
type: 'POST',
data: { APC_UPLOAD_PROGRESS: $('#progress_key').val() },
}).done(function (progressMeter) {
if (progressMeter) {
$('#progressbar').progressbar('value', parseInt(progressMeter));
}
});
}
</script>
</head>
<body>
<br />
<form method='post' enctype='multipart/form-data' id='uploadForm'>
<input type='file' name='uploadFile' style='width:400px;' id='uploadFile' />
<input type='hidden' name='APC_UPLOAD_PROGRESS' id='progress_key' value='539e79fc9249f' />
<input type='button' value='アップロード' onClick='javascript:formSubmit();'>
<div id='progressbar'>
<div id='loading'></div>
</div>
</form>
</body>
</html>
まず、jQueryやjQuery UIのソースコードはホストとされているものを使っていますが、この辺は自信の環境にあるのであれば読込み方を適宜変更してください。
進捗の表示は、HTML内のIDがprogressbarのDIV要素にプログレスバーが、その中のloadingのDIV要素に進捗割合が表示されます。
表示処理しているjQueryのコードの箇所は、
$('#progressbar').progressbar({
value: false,
change: function () {
$('#loading').text($('#progressbar').progressbar('value') + '%');
}
});
の部分となります。
valueに、進捗率を設定すればバーが100%に向けて伸びていきます。
厳密に言うと、「max」の項目を追加することで100%を幾つの数値にするか決められたりするので、valueで設定できるのは100までの数値とは限りません。
最初にfalseを設定しますが、これを設定すると先ほどのイメージの例の最初に表示されるような斜線のバーがぐるぐる回るようなアニメーションをするというだけで、あまり意味はないです。
「change」の設定項目はvalueの値が変化したときの処理を書きますが、画面上にその値を描画するように定義しています。
あとは、FormData()でフォームオブジェクトを作成し、APC_UPLOAD_PROGRESSの値とファイルデータをAjaxで送信しています。
APC_UPLOAD_PROGRESSは、マニュアルにも記載の通りこのパラメータがあればAPCはアップロードの進捗データを生成するようになります(このキーは、apc.rfc1867_name にて変更ができるみたいです)
ですので、APC_UPLOAD_PROGRESSの値をサーバーに送ることで、進捗データを生成させ、下記の進捗率を取得する関数でその値を受け取って画面上の割合を変化させています。
(詳細は、後述する進捗管理用のプログラムのところで触れています)
var progressBar = function () {
jqxhr = $.ajax('/progress.php', {
type: 'POST',
data: { APC_UPLOAD_PROGRESS: $('#progress_key').val() },
}).done(function (progressMeter) {
if (progressMeter) {
$('#progressbar').progressbar('value', parseInt(progressMeter));
}
});
}
続いて、送られたファイルのチェックやアップロード後の処理を行うプログラム(upload.php)ですが、この辺はシステム要件によってどんなチェックをするのかや、アップロードされたファイルをどこに保存するのかが変わってくるので、ここではかなり適当に書いています。
<?php
// 簡易的なエラーチェック
if (!empty($_FILES)) {
if (!empty($_FILES['uploadFile']['error'])) {
// エラーがあったら返すとか
echo $_FILES['uploadFile']['error'];
} else {
// アップロード成功したら適切な場所に保存するとか
move_uploaded_file($_FILES['uploadFile']['tmp_name'], "/tmp/" . $_FILES['uploadFile']['name']);
}
} else {
echo "Empty File";
}
最後は、進捗管理用のプログラム(progress.php)です。
<?php
$num = 0;
if (isset($_POST['APC_UPLOAD_PROGRESS'])) {
$status = apc_fetch('upload_' . $_POST['APC_UPLOAD_PROGRESS']);
if (isset($status['current'], $status['total'])) {
// 進捗計算
$num = strval(ceil($status['current'] / $status['total'] * 100));
}
}
echo $num;
こちらは、先ほど書いたようにAPC内のキャッシュデータから進捗データを読み取って計算して割合を返すということをしています。
キャッシュデータの読み込みは、APC_UPLOAD_PROGRESSの値によって行われますので、このキーはユーザーごとにユニークなものを割り振る必要があります。
先ほどのindex.php内で「539e79fc9249f」という値で送信していますが、この値をユニークになるように設定し埋め込んでおく必要があります。
また、apc_fetchにて進捗データを読込む際に、「upload_」の接頭辞を使っていますが、これもデフォルトのものでapc.rfc1867_prefix の設定項目によって変更することが可能です。
apc_fetchで受け取る配列のtotalキーがファイルの合計容量、currentが現在受け取っているデータサイズとなるので、ここから進捗を計算しているということになります。
今回はファイルアップロードの進捗表示ということで書いてみましたが、進捗を返せばjQueryのProgressbarウィジットでプログレスバーを表示することはできるので、処理内で進捗計算して値を返すようにすれば、その他の処理でも進捗表示をさせることができたりしますので応用してみてもよいかもしれません。
関連記事
[JS] Webアプリケーションでデスクトップへの通知機能を作る
jQuery UI Sortableを使ってドラッグ&ドロップでリストを並び替える
[jQuery] 外部JavaScriptファイル読込みに関する考察
Googleサジェストが実装できるjQuery Autocomplete Mod
Twitter入力フォームのように文字カウントしてくれるcharacter count
