最新のPHPニュース

PHPで大容量のファイルを操作する方法

2006年08月02日

ThinkPHPのブログにて、2GBを超えるファイルサイズを扱うための方法が紹介されています。通常のWebアプリケーションではそうそう扱う機会のないサイズでしょうが、バッチファイルなどでは確かに使う可能性があるかもしれません。

とりあえず、以下の2.2GBのファイルを作成したときに、どうなるかを考えてみましょう。

$ ls -hl dummyfile
-rw-r--r-- 1 johannes users 2.2G 2006-02-02 14:32 dummyfile

以下のPHPスクリプトで実行すると、どうなるでしょうか?

<php
 $fp = fopen("dummyfile", "r");
 $data = fread($fp, 255); fclose($fp);
?>

真っ白なページが表示されると思いきや、実際に実行してみると

Warning: fopen(dummyfile): failed to open stream: File too large in ....

このような警告が表示されます。そこで、ファイルサイズが一定値以上のものをブロックします。

<?php
 $file = "dummyfile";
 if (filesize($file) > 2147483647) {
  die("File to big");
 }
 $fp = fopen("dummyfile", "r");
 $data = fread($fp, 255); fclose($fp);
?>

もう一度実行してみると、

Warning: filesize(): stat failed for dummyfile in ...
Warning: fopen(dummyfile): failed to open stream: File too large in ...

また警告がでました。

filesize()関数ですら、このサイズのファイルは扱えないようです。そこで、fopen()関数と@オペレーターを使用して試してみましょう。@オペレータを使うと、指定した部分のエラーメッセージが表示されなくなります。

<?php
 $fp = @fopen("dummyfile", "r"); if (!$fp) { 
  die("データの処理に失敗しました");
 }
 $data = fread($fp, 255);
 fclose($fp);
?>

ただし、これでも大容量のファイルを処理する解決にはなりません。それを解く鍵は、どうしてこのような問題が生じるかを知っておくことです。

実は、PHPは動作するプラットフォームに、かなり依存しています。その例の1つが、いくつかの関数は特定のプラットフォームでは対応していないという点です。たとえば、checkdnsrr()関数はWindowsでは動作しません。

他にもプラットフォームに依存する問題があります。たとえば整数値のデータ型。PHPの実装では整数値はlong型に依存しており、至るところでこのデータ型が使用されています。今回の場合の原因は、オペレーティングシステムがファイルサイズをそのデータ型で返していることに由来します。しかし、ほとんどのOSでは大きなファイルサイズを扱える仕組みを備えているため、ファイル関数をその通りに修正することが望まれます。

コンパイル時に、-D_FILE_OFFSET_BITS=64をコンパイラのCFLAGS環境変数に加えることで、libcは標準的なstatコールではなくstat64コールを使用します。

具体的には、以下のように設定をしてPHPのconfigureを行います。

$ CFLAGS="-D_FILE_OFFSET_BITS=64" ./configure --必要なオプション・・・

では、テストしましょう。fopen()関数やfread()関数が問題なく動作しました。

しかし、まだ問題が残されています。filesize()関数の戻り値はInteger型なので、オーバーフローを起こします。

$ php -r 'echo filesize("dummyfile");'

の結果は、-1988084300となってしまいます。

他にも、seek()関数は全く機能しません。例えば、以下のプログラムです。

$ php -r '$fp = fopen("dummyfile", "r");
var_dump(fseek($fp, 2147483700));'

の結果は、int(-1)となります。

しかしながら、これで十分な場合もあるでしょう。

また、原文ではこの後この後、20GBを超えたCSVファイルを扱うための具体的なプログラムが掲載されています。具体的には、splitコマンドで分断し、それらをUNIXコマンドを駆使して処理をしています。上記の方法では満足できない場合、PHPでの処理をあきらめ、シェルコマンドやUnixコマンドを組み合わせることが良いみたいです。

関連リンク

関連ニュース

この記事へのトラックバックURL

まつぼっくりんご

2008年11月05日 15:50 PHPで2GB以上のファイルを扱う

x86のLinuxにてPHPを普通にコンパイルすると2GBま...

続きを読む