fopen
で非常に適度なサイズのファイルを読み取ろうとすると、PHP
が失敗します。 A 6 meg file
を使用すると狭くなりますが、100k
の周りの小さいファイルは問題ありません。 20ギガを超えるファイルやとんでもないファイルを読み取るためにPHP
を-D_FILE_OFFSET_BITS=64
フラグで再コンパイルする必要がある場合があることを読みましたが、6 MBのファイルで問題はないはずです。 ?最終的には、100 MB程度のファイルを読み込みたいと思います。小さいファイルでできるので、それらを開いて、fgetsで1行ずつ読み取ることができればいいでしょう。
PHP
の非常に大きなファイルの読み取りと操作を行うためのトリック/ソリューションは何ですか?
更新:6 megファイルで失敗する単純なコードブロックの例を次に示します-PHPはエラーをスローしないようで、単にfalseを返します。たぶん私は何か非常に馬鹿げたことをしていますか?
$rawfile = "mediumfile.csv";
if($file = fopen($rawfile, "r")){
fclose($file);
} else {
echo "fail!";
}
別の更新:皆さんの助けに感謝します、それは信じられないほどおかしなものであることが判明しました-アクセス許可の問題です。私の小さなファイルには、大きなファイルにはないのに、どうしても読み取り権限がありました。どー!
スクリプトのタイムアウト設定ではなく、失敗しているのはfopen
ですか?デフォルトは通常約30秒程度であり、ファイルの読み取りにそれよりも時間がかかる場合は、トリップする可能性があります。
考慮すべきもう1つのことは、スクリプトのメモリ制限である可能性があります。ファイルを配列に読み込むと、これが失敗する可能性があるため、エラーログでメモリ警告を確認してください。
上記のいずれも問題でない場合は、 fgets
を使用してファイルを1行ずつ読み取り、処理を進めてみてください。
$handle = fopen("/tmp/uploadfile.txt", "r") or die("Couldn't get handle");
if ($handle) {
while (!feof($handle)) {
$buffer = fgets($handle, 4096);
// Process buffer here..
}
fclose($handle);
}
編集
PHPはエラーをスローしないようで、単にfalseを返します。
$rawfile
へのパスは、スクリプトが実行されている場所に対して相対的ですか?ファイル名に絶対パスを設定してみてください。
1.3GBファイルと9.5GBファイルで2つのテストを行いました。
1.3 GB
fopen()
を使用する
このプロセスでは、計算に15555 msを使用しました。
システムコールに169ミリ秒かかりました。
file()
を使用する
このプロセスは、計算に6983 msを使用しました。
システムコールに4469ミリ秒かかりました。
9.5 GB
fopen()
を使用する
このプロセスでは、計算に113559 msを使用しました。
システムコールに2532ミリ秒かかりました。
file()
を使用する
このプロセスでは、計算に8221 msを使用しました。
システムコールに7998ミリ秒かかりました。
file()
の方が速いようです。
•fgets()
関数は、テキストファイルが20 MBを通過し、解析速度が大幅に低下するまで問題ありません。
•file_ get_contents()
関数は、40 MBytesまでは良好な結果を、100 MBytesまでは許容可能な結果を提供しますが、-file_get_contents()
はファイル全体をメモリにロードするなので、スケーラブルではありません。
•file()
関数は、テキストの各行を含む配列を作成し、この配列がメモリに格納され、使用されるメモリがさらに大きくなるため、テキストの大きなファイルでは悲惨です。
実際、200 MBのファイルを解析できたのは、_memory_limit
_を2 GBに設定した場合のみでした。これは、解析しようとした1 GB以上のファイルには不適切でした。
1 GBを超えるファイルを解析する必要があり、解析時間が15秒を超えていて、ファイル全体をメモリにロードしたくない場合は、別の方法を見つける必要があります。
私の解決策は任意の小さなチャンクでデータを解析するでした。コードは次のとおりです。
_$filesize = get_file_size($file);
$fp = @fopen($file, "r");
$chunk_size = (1<<24); // 16MB arbitrary
$position = 0;
// if handle $fp to file was created, go ahead
if ($fp) {
while(!feof($fp)){
// move pointer to $position in file
fseek($fp, $position);
// take a slice of $chunk_size bytes
$chunk = fread($fp,$chunk_size);
// searching the end of last full text line
$last_lf_pos = strrpos($chunk, "\n");
// $buffer will contain full lines of text
// starting from $position to $last_lf_pos
$buffer = mb_substr($chunk,0,$last_lf_pos);
////////////////////////////////////////////////////
//// ... DO SOMETHING WITH THIS BUFFER HERE ... ////
////////////////////////////////////////////////////
// Move $position
$position += $last_lf_pos;
// if remaining is less than $chunk_size, make $chunk_size equal remaining
if(($position+$chunk_size) > $filesize) $chunk_size = $filesize-$position;
$buffer = NULL;
}
fclose($fp);
}
_
使用されるメモリは_$chunk_size
_のみであり、速度はfile_ get_contents()
で取得されるものよりもわずかに遅くなります。 PHPグループは、解析機能を最適化するために私のアプローチを使用する必要があります。
*)get_file_size()
関数を検索 here 。
ファイルを出力するだけの場合は、readfile関数を使用してみてください。
そうでない場合-アプリケーションの設計について考える必要があるかもしれませんが、Webリクエストでそのような大きなファイルを開くのはなぜですか?
私はfopenを使用して、phpスクリプトをビデオストリーミングサーバーとして使用し、ストリーミング用のビデオファイルを開きました。サイズが50/60 MBを超えるファイルでも問題ありませんでした。
私にとって、fopen()
は1MBを超えるファイルで非常に遅くなり、file()
ははるかに高速です。
一度に100行ずつ読み取ってバッチ挿入を作成しようとすると、fopen()
は37秒かかります。vsfile()
は4秒かかります。 file()
に組み込まれているstring->array
ステップである必要があります
すべてのファイル処理オプションを試して、どちらがアプリケーションで最適に機能するかを確認します。
問題がメモリ制限に達したことが原因である場合は、それをより高い値に設定してみてください(これは、phpの構成によっては機能するかどうかにかかわらず)。
これにより、メモリ制限が12 Mbに設定されます。
ini\_set("memory_limit","12M");