PHP5.4の新機能の一つ: アップロード進捗の取得

PHP5.4 Advent Calendarの13日目です。@co3kさんの記事に引き続き、PHP5.4の新しい機能を紹介します。

PHP5.4で見逃せない新機能の1つとして、今までPHP単体では出来なかったファイルのアップロードの進捗情報が取得できるようになりました。今までアップロードの進捗情報は、APCFlashなどを利用しなければ取得できないものでした。この記事では実際に動くサンプルコードと共にこの機能を紹介します。

導入

以下のようなファイルアップロードのためのフォームを用意します。この時パラメータにsession.upload_progress.nameディレクティブのエントリを追加します。これはアップロードの進捗情報を取得するという宣言で、これが無いと進捗は保存されないので注意です。この例では、"example"という値をつけています。

<?php session_start() ?>
<form action="upload.php" target="hoge" method="POST" enctype="multipart/form-data">
    <input type="hidden" name="<?php echo ini_get("session.upload_progress.name"); ?>" value="example" />
    <input id="file1" type="file" name="file1" />
    <input type="submit" value="upload" />
</form>

以上のフォームから何かでっかいファイルをアップロードすると、セッション内にアップロードの進捗が設定されるようになります。アップロードしつつ、以下のプログラムにアクセスすると、進捗情報がダンプされます。

<?php 
session_start();
$key = ini_get("session.upload_progress.prefix") . "example";
var_dump($_SESSION[$key]);

session.upload_progress.prefixディレクティブ + 先ほどつけた"example" というキーでセッション中に進捗情報が保存されます。上の例だと以下のように設定されます。

array(5) {
  ["start_time"]=>
  int(1323713434)
  ["content_length"]=>
  int(155908)
  ["bytes_processed"]=>
  int(155908)
  ["done"]=>
  bool(true)
  ["files"]=>
  array(1) {
    [0]=>
    array(7) {
      ["field_name"]=>
      string(5) "file1"
      ["name"]=>
      string(13) "APC-3.1.9.tgz"
      ["tmp_name"]=>
      string(22) "/private/tmp/phpeMGDne"
      ["error"]=>
      int(0)
      ["done"]=>
      bool(true)
      ["start_time"]=>
      int(1323713434)
      ["bytes_processed"]=>
      int(155540)
    }
  }
}

各種情報が保存されていることがわかると思います。100 * (content_lengthパラメータ / byte_processedパラメータ) で、アップロード全体がどれぐらい進んだかをパーセントで求められます。また、各ファイルごとの進捗もここで取得することができます。

実際に進捗付きのアップロードフォームを作る場合は、Ajaxで進捗を取得して表示することになると思います。以下はjQueryを使って進捗を%で表示する例です。

<?php
// index.php
session_start();
?><html><head></head>
<body>

<form action="index.php" method="POST" enctype="multipart/form-data">
    <input type="hidden" name="<?php echo ini_get("session.upload_progress.name"); ?>" value="example" />

    <input id="file1" type="file" name="file1" />
    <input type="submit" value="upload" />
    <span id="progress">0%</span>
</form>

<script type="text/javascript" src="http://code.jquery.com/jquery-1.7.1.min.js"></script>
<script type="text/javascript">

$(function() {
    $("form").submit(function() {
        var progress = $("#progress");

        var f = function() {
            $.getJSON("./progress.php", function(data) {
                console.dir(data);

                if (data != null) {
                    progress.text(
                        "" + Math.round(100 * (data["bytes_processed"] / data["content_length"])) + "%"
                    );
                    if (!data["done"]) {
                        setTimeout(f, 200);
                    }
                } 
            });
        };

        setTimeout(f, 300);
    });
});

</script>

</body></html>
<?php
# progress.php

session_start();
$key = ini_get("session.upload_progress.prefix") . 'example';
echo isset($_SESSION[$key]) ? json_encode($_SESSION[$key]) : json_encode(null);

以下はサンプルを作成した際にphp.iniに追加で設定したディレクティブです。

upload_max_filesize = 2000M
error_reporting = E_ALL
session.upload_progress.min_freq = 0.000000001
session.upload_progress.cleanup = off 
post_max_size = 2000M
memory_limit = 2000M

各種ディレクティブは、実際に本番で利用する際は適切に設定して下さい。特にsession.upload_progress.cleanupはセッション中にゴミを残さないようにonにしたほうがいいと思います。

終わり

というわけで、PHP5.4のファイルアップロードの進捗を取得するコードを紹介しました。次は@tpyamamotoさんお願いいたしますー。