Web 2.0 はインターネットで最も注目を浴びている流行語で、投資者たちは Web 2.0 の名前を冠したあらゆるものに投資しようと列を作っています。数百万のWeb サイトとそこでホストされるアプリケーションを記述する用語はたくさんありますが、Web 2.0 を説明すると、数百万のインターネット・ユーザーに意見を表明する手段を提供するWeb サイトの 1 つのカテゴリーということになります。ユーザーが集まって共通の興味に関する意見とデータを共有する場を提供するという点で独特なWeb 2.0 のサイトでは、急速に大量のコンテンツが生成されています。
ユーザーのそれぞれが、コーヒー・ショップの感想や会社への道順など、何らかのコンテンツを提供します。その好例が、個人がビデオをアップロードして、他のユーザーからそのビデオを見た感想をもらう場を提供するYouTube です。YouTube は目下、Web 2.0 を観察する人々のお気に入りとなっています。かつて、これほどまでに短時間で人気を集めたインターネット・サイトはありませんでした。この人気の理由は、ありとあらゆる種類のコンテンツ、そしてユーザーがコメントという形でコンテンツに対する意見を言えることにあります。単なるコメントだけではありません。ユーザーはビデオに対してコメントしたビデオをアップロードすることさえできるのです。
ファイルを受け付ける多くの Web サイトでは非常に恐ろしい Browse ボタンを誇らしげに見せびらかしていますが、その横にはユーザーにファイルを一度に 1 つずつアップロードするよう注意するテキスト・フィールドがあります。ビデオの場合は言うまでもなく、写真、それに小さなファイルの集まりで構成される他のアイテムでさえもファイルをアップロードするには時間がかかります。それぞれのファイルは特有のアップロード方法が必要なため、アップロードするのはかなり厄介な作業で、大きなファイルのアップロードともなると、せっかちなユーザーはうんざりしかねません。ここで重要になるのが、ユーザーがアップロードを諦めてサイトから出て行かないように、フィードバックを提供して明るい見通しを持たせることです。
幸い、ファイル・アップロード・プロセスに追加する PHP V5.2 の新しいフックを使えば、ユーザーにリアルタイムのアップロード進行状況を表示することが可能です。そこで、この記事ではPHP V5.2 を使用してユーザーのための進行状況表示バーを作成する方法を紹介します (ソース・コードについては「ダウンロード」セクションを参照)。
PHP V5.2 の新しい「フック」は実際にはデータ・ポイントで、正しいライブラリーがインストールされて構成されていればファイル転送プロセス中に使用できます。これらのフックが使用するのは、APC(Alternative PHP Cache) と呼ばれる機能です。PHP スクリプトがアップロードされたファイルを受け取ると、インタープリターは自動的に$_POST 配列で APC_UPLOAD_PROGRESS という名前の隠しフィールドを調べます。この隠しフィールドがキャッシュされる変数となって、アップロードに関する情報を格納し、スクリプトでアクセスできるようにします。情報をキャッシュに入れてすぐに利用できるようにすることで、ユーザーに視覚的フィードバックを表示してユーザー・エクスペリエンスの改善を図ることが可能になります。
この記事では、PHP 内での APC コードの識別に加え、HTML フォームへの APC コードの実装について説明し、またキャッシュ情報にアクセスする方法についても説明します。このデータを表現する方法はAjax から FLEX に至るまでさまざまですが、この記事では、これらのフロンドエンド技術に必要となるデータへのアクセスを準備する方法に焦点を絞ることにします。
PHP V5.2 のデフォルトでは、APC は有効に設定されていません。新規フックは APC の一部なので、この拡張をインストールして PHPインタープリターから確実に使用できるようにする必要があります。それには、php_apc 拡張ファイルをダウンロードしてください。この記事で使用しているのはWAMP システムです。WAMP は Windows® 向けに無料で入手できるパッケージ化された PHP で、Apache およびMySQL が組み込まれています。WAMP は優れたユーザー・インターフェースを持つだけでなく、構成オプションをサポートするメニューの管理も簡単です。
APC を WAMP でセットアップする手順は以下のとおりです。
- 「参考文献」を参照して、ライブラリーと WAMP をダウンロードします。
- WAMP をインストールします。
- php_apc.dll ファイルを PHP の拡張フォルダーに配置します。拡張フォルダーの場所は、デフォルトでは <wamproot>/php/extとなっています。
- システム・トレイの WAMP メニューで、PHP settings>PHP Extensions>Add Extension を選択します。
- ポップアップ表示されたコマンド行インターフェースに
php_apc.dllと入力し、Enter を押します。 - テキスト・エディターを使って <wamproot>/php/php.ini を開き、
apc.rfc1867 = onという行を追加します (場所はどこでも構いません)。ローカル側でテストして、大きなサイズのファイルをアップロードする際の進行状況を実際に見てみたいという場合は、ディレクティブとしてapc.max_file_size = 200M、upload_max_filesize = 200M、およびpost_max_size = 200Mも追加します。ただし、稼働中の実動サーバーではこのディレクティブを追加しないでください。帯域幅とディスク・スペースの割り当てを使い果し、言うまでもなく他のユーザーの処理速度を落とすことになってしまいます。 - PHP を再起動します。
これで、APC のセットアップと初期化は完了です。APC の RFC1867 機能 (ファイル・アップロードの追跡を可能にする機能) がオプションとして有効になり、ファイル・アップロードを調べてリアルタイムの状況を表示できるようになります。
ファイルを受信するには、まず、ファイルを受信するためのフォームをセットアップする必要があります。都合のよいことに、HTML にはファイル用の標準フィールド・タイプが用意されています。すべてのHTML フォームのフィールドと同じく、このタイプにも論理的に file という型の名前が付けられています。また、デフォルトでブロックの右側に表示される便利な Browse ボタンもあります。
リスト 1. upload.php の HTML フォーム
<?php
$id = $_GET['id'];
?>
<form enctype="multipart/form-data" id="upload_form"
action="target.php" method="POST">
<input type="hidden" name="APC_UPLOAD_PROGRESS"
id="progress_key" value="<?php echo $id?>"/>
<input type="file" id="test_file" name="test_file"/><br/>
<input onclick="window.parent.startProgress(); return true;"
type="submit" value="Upload!"/>
</form>
|
このフォームには PHP ページを作成する必要があります。アップロードを追跡するには固有のキーが必要となるためです。最終的にはこのキーが、ページを呼び出すために使用するURL に GET値として組み込まれます。GET 値は、後で取得する APC キャッシュのエントリー・キーの値となります。この値を渡すためにフォームのフィールドに必要となるのが、APCにファイルのアップロード状況を格納する必要があることを認識させるための特殊な名前を持つ隠しフィールドです。このフィールドの名前は APC_UPLOAD_PROGRESSで、これが前述したキャッシュ・プロセスを開始するフックです。PHP がキャッシュ内の正しい項目にアクセスすることを確実にするため、隠しフィールドの値として取得した固有のID を使用してこの値のキーを作成します。ユーザーがフォームを送信すると (送信ボタンについてはこの後説明します)、ブラウザーはファイルとともに、このキーをサーバーに送信されるPOSTデータの一部として送信します。
このページがブラウザーにロードされると、図 1 のとおり、非常に簡単なフォームが表示されます。
図 1. アップロード・フォーム
ユーザーがページ全体をリロードせずにファイルを送信できるようにするため、このフォームは別のファイル内の iframe に組み込むことになります。フォームのアクション・ページ (target.php) だけを使用してデータを取得しようとしても、キャッシュ情報を表示することはできません。このページはアップロードが完了するまで情報を返さないからです。そのため、この新しいフックの最も一般的な使用例はAjax で作成されています。Ajax で作成すると、フォームを送信してからもウィンドウを最新表示せずに同じウィンドウでアップロードの状況をそのまま調べることができます。
このスクリプトを動作させるには、次に収容ページに取り掛かります。このページで iframe をセットアップし、アップロードされたファイルの情報を受信します。また、進行状況インジケーターのためのデータを取得してこのインジケーターの状況を表示する一連のJavaScript 関数も必要です。
送信されたフォームにファイルが含まれている場合、そのファイルは永久的な場所に保管されるまでいったん、サーバーの一時的な場所に送信されます。一時ストレージ内のファイルは、$_FILES連想配列から利用できます。PHP での標準的なファイル・アップロード関数を使用して、パスを選択してサーバーに保管することも、あるいは処理することもできます。
リスト 2. target.php ファイル
<?php
if($_SERVER['REQUEST_METHOD']=='POST') {
move_uploaded_file($_FILES["test_file"]["tmp_name"],
"c:\\sw\\wamp\\www\\" . $_FILES["test_file"]["name"]);
echo "<p>File uploaded. Thank you!</p>";
}
?>
|
上記ではまず、POST 変数 (フォームから渡される) が設定されているかを調べて、フォーム・データを受信したことを示しています。フォーム・データを受信して、それがファイルであることを期待する場合は、グローバル配列$_FILES も使用する必要があります。アップロードされたファイルは、そのファイルで何を実行するかに応じて安全な場所に移します。この例では、ファイルを単に\sw\wamp\www に移しているだけです (これはもちろん、まったく任意の場所なので、自由に場所を選んでください)。この一連のアクションを完了した時点で、ユーザーに感謝の言葉を述べています。
ここではほぼ完全を期して実際のファイル処理を含めていますが、この記事の話題は進行状況表示バーなので、実際のファイルを受信したときに何を行うかは問題ではありません。
そろそろ、実際のアップロード進行状況を返すスクリプトも必要になってきました。リスト 3 に、極めて単純なバージョンを示します。
リスト 3. getprogress.php ファイル
<?php
if(isset($_GET['progress_key'])) {
$status = apc_fetch('upload_'.$_GET['progress_key']);
echo $status['current']/$status['total']*100;
}
?>
|
上記のスクリプトではまず、前述した $id 値である progress_key を検索しています (どこから渡されるのかはすぐにわかるのでご心配なく)。次に apc_fetch() を呼び出します。これは、APC キャッシュからデータを返す関数です。正しいファイル情報が必要なので、ここでは $_GET['progress_key'] として表される固有の ID が必要となります。この固有の ID に相当するのが、apc_fetch() の呼び出しで指定しているパラメーター upload_xxxxxx の xxxxxx の部分です。upload_ の部分はPHP によって自動的に前に付加されます。
データを取得したら、今度は JSON 拡張を使って JavaScript で使いやすいフォーマットに情報を設定し、必要であればオブジェクト全体を返します。$status オブジェクトは、以下のフィールドを持つ配列です。
-
total - ファイルの合計サイズ
-
current - 現時点までに受信したファイルのサイズ
-
rate - 1 秒ごとのバイト数で表したアップロード速度
-
filename - ファイルの名前
-
name - 変数の名前
-
temp_filename - PHP がファイルの一時コピーを保管している場所
-
cancel_upload - アップロードがキャンセルされたか (1)、またはキャンセルされていないか (2)
-
done - アップロードが完了したか (1)、または完了していないか (2)
この例で必要なのはアップロードが完了したパーセンテージですが、必要に応じてこれ以外の情報も選択できます。
いよいよ実際の進行状況表示バーを作成する段階にきました。作業を単純にするため、このスクリプトではリスト 4 に示すように、CSS を使ってバーをエミュレートするdiv を作成します。この div は JavaScript で制御することができます。
リスト 4. メインの progress.php ファイル
<html>
<head><title>Upload Example</title></head>
<body>
<script type="text/javascript">
var counter = 0;
function startProgress(){
document.getElementById("progressouter").style.display="block";
fire();
}
function fire(){
if (counter < 101){
document.getElementById("progressinner").style.width =
counter+"%";
counter++;
setTimeout("fire()",100);
}
}
</script>
<div id="progressouter" style=
"width: 500px; height: 20px; border: 6px solid red; display:none;">
<div id="progressinner" style=
"position: relative; height: 20px; background-color: purple; width: 0%; ">
</div>
</div>
<span onclick="startProgress()">Start me up!</span>
</body>
</html>
|
このページは、外枠として機能する div の内側に、もう 1 つ div 要素が含まれています。スクリプトは外枠を基準に内部 div をサイズ変更して進行状況を表示します。ユーザーが Start me up!テキストをクリックすると、startProgress() スクリプトは fire() 関数を呼び出します。この関数はカウンターの値をチェックし、値が 100 を超えていなければ内部 divを外側 div 幅の該当するパーセンテージに設定します。次にカウンターをインクリメントして、ブラウザーにこの一連の動作を 1/10 秒で繰り返すように指示します。
結果は図 2 のように表示されます。
図 2. 進行状況表示バーのスクリプト
あと必要なのは、スクリプトに任意の値ではなく、完全なパーセンテージで幅を更新させるようにするための方法だけです。
残るは、すべてを組み立てることだけです。それには、progress.php ページを操作します。
リスト 5. 最終的な progress.php ページ
<?php
$id = uniqid("");
?>
<html>
<head><title>Upload Example</title></head>
<body>
<script src="http://maps.google.com/maps?file=api&v=2&key=<yourkeyhere>"
type="text/javascript"></script>
<script type="text/javascript">
function getProgress(){
GDownloadUrl("getprogress.php?progress_key=<?php echo($id)?>",
function(percent, responseCode) {
document.getElementById("progressinner").style.width = percent+"%";
if (percent < 100){
setTimeout("getProgress()", 100);
}
});
}
function startProgress(){
document.getElementById("progressouter").style.display="block";
setTimeout("getProgress()", 1000);
}
</script>
<iframe id="theframe" name="theframe"
src="upload.php?id=<?php echo($id) ?>"
style="border: none; height: 100px; width: 400px;" >
</iframe>
<br/><br/>
<div id="progressouter" style=
"width: 500px; height: 20px; border: 6px solid red; display:none;">
<div id="progressinner" style=
"position: relative; height: 20px; background-color: purple; width: 0%; ">
</div>
</div>
</body>
</html>
|
下から上に向かって説明すると、リスト 1 の upload.php スクリプトを組み込む iframe を追加して、ページの先頭で生成された固有 ID を指定しています。
ここで、送信ボタンは以下のフォームであることに注目してください。
<input onclick="window.parent.startProgress(); return true;" type="submit" value="Upload!"/> |
このボタンは 2 つのことを行います。まず、このボタンは標準の送信ボタンと同じようにフォームを送信しますが、その前に、メインウィンドウで startProgress() スクリプトを呼び出します。startProgress() スクリプトは進行状況表示バーにそれ自体を表示させるように指示し (バーは最初、表示プロパティーなしで開始します)、次にブラウザーに 1 秒待機してからgetProgress() スクリプトを実行するように指示します。
面白くなってくるのは、この getProgress() スクリプトからです。前にも言ったように、ファイルの進行状況を調べるには、Ajax や同様のメソッドを使用する必要があります。そこで、この例ではフォームがショートカットを使用してGoogle Maps API ライブラリーから GdownloadUrl() 関数を呼び出しています (フォームはライブラリーをページの先頭にインポートすることに注意してください。このライブラリーには独自のキーを取得する必要がありますが、キーはGoogleから無料で入手できます)。
GdownloadUrl() 関数は、URL のコンテンツ (この例では getprogress.php スクリプト) をダウンロードして、そこに定義された匿名関数を実行します。この関数が受け入れる最初のパラメーターは、URLから返されたデータです。この例でのデータはパーセンテージなので、それを使って進行状況表示バーを更新します。最後に、ファイルのダウンロードがまだ完了してない場合は、ブラウザーに1/10 秒で再試行するように指示します (実際には、それほど速くこれらの呼び出しを実行することはできないかもしれませんが、ブラウザーはできる限りの速さで実行します)。
最終的なページでは、ユーザーがファイルのアップロードの進行状況を見て取ることができます。
図 3. Progress.php からの出力
Web 2.0 の世界でユーザーに推奨されるのは、Web サイトでユーザーが互いに情報とコンテンツを提供し合うことです。個人対個人のこのオープンで自由なデータ交換に対応したフレームワークは、私たち開発者が作成します。これを可能にするツールはかねてから用意されていますが、ユーザー・エクスペリエンスという点ではまだその可能性が十分に発揮されていません。この記事を読んで、サイトへの情報のアップロードに関する進行状況表示バーを表示してリアルタイムのフィードバックをユーザーに提供し、ユーザー・エクスペリエンスとアプリケーションの品質を改善する方法がわかったはずです。
| 内容 | ファイル名 | サイズ | ダウンロード形式 |
|---|---|---|---|
| Sample jar files | os-php-v525.source.zip | 3KB | HTTP |
学ぶために
-
PHP 5.2.0 Release Announcementを読んでください。
-
Upload フックのデモを試してみてください。
-
PHP.netの PHP 資料で JSONについて調べてください。
- IBM developerWorks Ajax resource centerにアクセスしてください。
-
「Learning PHP, Part 2: Upload files and use XML to store and display fileinformation」は、ブラウザーからのファイル・アップロード、セッション、あるいは PHP を使用して XML を処理する方法についてのチュートリアルです。
-
Google Maps APIの詳細を読んでください。
- PHP の情報源、PHP.netにアクセスしてください。
-
「PHP V5 マイグレーション・ガイド」を読んで、PHP V4 で作成したコードを V5 にマイグレーションする方法を学んでください。
-
PHP.netは、PHP 開発者のための情報源です。
-
「Recommended PHP reading list」を読んでください。
- developerWorks ですべての PHP 関連記事を調べてください。
- IBM developerWorks のPHP project resourcesにアクセスして、PHP の腕を磨いてください。
- ソフトウェア開発者を対象とした興味深いインタービューや討論については、developerWorks ポッドキャストをチェックしてください。
- developerWorks のTechnical events and webcastsで最新情報を入手してください。
- PHP でデータベースを使用するには Zend Core for IBMを調べてみてください。シームレスにすぐに使えて、インストールも簡単なこの PHP 開発および実動環境は、IBM DB2 9 をサポートします。
- 世界中で近日中に予定されている IBM オープン・ソース開発者を対象とした会議、見本市、ウェブ放送やその他のイベントをチェックしてください。
- オープン・ソース技術を使用して開発し、IBM の製品と併用するときに役立つ広範囲のハウツー情報、ツール、およびプロジェクト・アップデートについては、developerWorksOpen source ゾーンを参照してください。
議論するために
-
Windows 対応 APC をダウンロードしてください。
- その他のプラットフォームに対応する APCはここからダウンロードできます。
-
WAMPをダウンロードしてください。
-
Google Maps API キーにサインアップしてください。
-
IBM ソフトウェアの試用版を使用して、次のオープン・ソース開発プロジェクトを革新してください。ダウンロード、あるいは DVD で入手できます。
-
IBM 製品の評価版をダウンロードして、DB2®、Lotus®、Rational®、Tivoli®、および WebSphere®のアプリケーション開発ツールとミドルウェア製品を使ってみてください。
-
developerWorks blogsから developerWorks コミュニティーに加わってください。
-
PHP Forum: Developing PHP applications with IBM Information Managementproducts (DB2, IDS)に参加してください。