(PHP) 大容量のファイルを、ブラウザが落ちないように何度かに分けて処理する

以前、MySQLSQLiteへ変換するプログラムを作りました。
http://d.hatena.ne.jp/sutara_lumpur/20120714/1342269933


が、たった数MBのSQLファイルでも、変換中にブラウザが落ちてしまうことがあります。
"fgets"で1行ずつ処理しているのですが…(-_-;)
そこで、1度に全て変換せず、何度かに分けることにしました。


まずは、処理を単純化したサンプルを作ってみます。
1度に変換するなら下記のようにwhile文ひとつで済みます。


(PHP)
$fp = fopen('test.txt', 'r');
while ($line = fgets($fp)) {
//何らかの変換処理
}
これを、3行ごとに休憩を入れながら処理するようにします。

(PHP)
$pos = 0;
while (1 == 1) { //無限ループ
$fp = fopen('test.txt', 'r');
fseek($fp, $pos);
for ($i=0; ($line = fgets($fp)) && ($i < 3); $i++) {
//何らかの変換処理
$pos = ftell($fp);
}
if (feof($fp)) {
fclose($fp);
break; //ループ終了
}
fclose($fp);
}
あくまでサンプルなので、便宜上無限ループを使っています。
変数$posに現在のファイルポインタの位置を記憶させることで、ファイルを閉じても前回の続きから行を取得できるようにしています。
現在のファイルポインタを取得する"ftell"、
任意の場所にファイルポインタを移す"fseek"が活躍してくれます。


※ for文の中で"ftell"を使っているのは、ループの条件判断の際に、その後ループを継続するしないに関わらず"fgets"で1行読み込んでしまっているためです。
つまり、ループを抜けた場合は、必ず1行余分に読み込んでいることになるわけです。
なので、ループ中に"ftell"する必要があります。


では本番として、jQueryを管制塔として、Ajaxで何度かに分けて実行する処理を考えます。
まずは、途中経過を伝えるエリアと、変換結果を表示するエリアをHTML中に作ります。


(HTML)
<textarea id="msg"></textarea>
<textarea id="result"></textarea>
次はJavaScript
jQueryを使ってます。

(jQuery)
function getFileBitByBit(pos) {
$.getJSON(
'convert.php',
{pos : pos},
function(json) {
//メッセージ
$('#msg').val($('#msg').val() + '一旦休憩(*´∀`*)\n');
$('#result').val($('#result').val() + json.text);

//最後まで読み込んでいなければ、再び実行
if (!json.feof) getFileBitByBit(json.pos);
}
);
}
//初回実行
getFileBitByBit(0);

そしてPHP

(PHP)
//JavaScriptへ返す配列を準備
$return = array(
'text' => '',
'feof' => false,
'pos' => $_GET['pos']
);
$fp = fopen('test.txt', 'r');
fseek($fp, $return['pos']);
for ($i=0; ($line = fgets($fp)) && ($i < 3); $i++) {
$return['text'] .= $line; //実際は、ここで何らかの変換を行う
$return['pos'] = ftell($fp);
}
$return['feof'] = feof($fp);
fclose($fp);
echo json_encode($return);
これで、ブラウザを落とすことなく大容量のファイルを扱える…と思います。
まだ実際には試してないので…(^ ^;)