PHPでリモートファイル(httpやsshで読み込むファイル)をfreadする際、容量の大きいファイルを読み込むと、読み込みが途中で切れる事があります。
読み込みファイルの途中で途切れるだけで、エラーなどが発生しないのがやっかいなところ。
これはfreadの仕様でして、リモートファイルを読み込む場合は別の方法をで実装した方が良いです。
読み込みが切れる理由と、正しい実装方法をご紹介します。
【理由】リモートファイルをfreadすると途中でファイル読み込みが切れる
リモートファイルをfreadすると、途中でファイル読み込みが切れる事があります。
恐ろしいのは、そこそこのファイルサイズじゃないと切れないので大きいサイズのファイルでテストしていないと気づかないんですよね。
テストをスルーして本番で不具合発覚なんてケースになりえるので要注意です。
なぜファイルの読み込みが途中で切れるかは、PHPリファレンスに記載がありました。
警告
通常のローカルファイル以外のもの、例えば リモートファイル や popen()、fsockopen() が返す ストリームを読み込んでいる場合には、 パケットが有効になった後に読み込みはストップします。 つまり以下の例のように分割されたデータを結合すべきであるということです。
PHPリファレンス
リモートファイルをfreadする場合は、パケットの単位で読み込みが途切れてしまうのです。
ちなみに、リファレンスではhttpでファイルを開いたケースが紹介されいますが、僕はssh2で読み込んだファイルで発生しました。
リモートファイルの読み込み全般で発生すると思われます。
【対策】freadではなくstream_get_contentsを使おう
正しい実装方法もリファレンスに書いてあります。
解決方法をPHPリファレンスから引用します。
PHP5以降
<?php
// PHP 5 以降での例
$handle = fopen("http://www.example.com/", "rb");
$contents = stream_get_contents($handle);
fclose($handle);
?>
PHP5未満
<?php
$handle = fopen("http://www.example.com/", "rb");
if (FALSE === $handle) {
exit("Failed to open stream to URL");
}
$contents = '';
while (!feof($handle)) {
$contents .= fread($handle, 8192);
}
fclose($handle);
?>
PHP5以降であれば、freadをstream_get_contentsに変えればOKです。
PHP5未満の場合は、読み込み完了をチェックしながらループで繰り返しfreadする必要があります。
まとめ:リモートファイルの読み込みにfreadを使う場合は要注意
こういう不具合はテストで見つけられなかったりするので、ちょっと怖いですよね。
実際に僕はこれで不具合を出してしまいました。
この記事が少しでも参考になれば幸いです。
最後までお読み頂きありがとうございました。