PHPExcelで大きいエクセルファイルを読み込んだときメモリ不足
PHPExcelはメモリ喰い
PHPExcelで、比較的大きなファイルをロードしたとき、メモリ不足となることがある。
Fatal error: Allowed memory size of NNNNNNNNN bytes exhausted (tried to allocate NN bytes) in path_to_PHPExcel_class
これは割と頻繁に起こる。PHPExcelはメモリ喰いなのだ。
メモリの消費は、行の多さより列の多さの方が逼迫するように思うがどうだろうか?(多パターン未検証)。
ファイルサイズ | 列数 | 行数 | 使用メモリ |
13k | 23 | 26 | 4,486,800 bytes |
86k | 23 | 1026 | 218,604,216 bytes |
84k | 1023 | 26 | 222,359,312 bytes |
PHPExcelは、エクセルファイルをロードするときにデフォルトではメモリ上にセルの内容をキャッシュする。
このため大きなファイルを読み込むとメモリ不足となる。
対応方法
これに対応するにはいくつかのアプローチがある。
PHPの使用するメモリサイズを変更できる場合
- php.iniでmemory_limitの値を設定し恒久的に変更
- ini_set で一時的に変更
・・・・・・ ini_set('memory_limit', '1G'); ・・・・・・
PHPの使用するメモリサイズを変更できない場合
- PHPExcelのキャッシュストレージをメモリからディスクなどに変更する
- PHPExcel_CachedObjectStorageFactoryには、いくつかのストレージが選択できるようだ。
- cache_in_memory これがデフォルト。メモリにセルオブジェクトをそのままメモリに保存
- cache_to_discISAM 一時ファイルに保存
- 他にも以下のようなクラスがあった。要件に応じて使い分けられたい。
- cache_in_memory_serialized
- cache_in_memory_gzip
- cache_igbinary
- cache_to_phpTemp
- cache_to_apc
- cache_to_memcache
- cache_to_wincache
- cache_to_sqlite
- cache_to_sqlite3
例えば一時ファイルに保存するには以下のようにする。
・・・・・・ // キャッシュ方法を一時ファイルに保存する $cacheMethod = PHPExcel_CachedObjectStorageFactory::cache_to_discISAM; // 一時ファイルの場所を指定 $cacheSettings = array('dir' => '/tmp'); // 設定を反映 if (!PHPExcel_Settings::setCacheStorageMethod($cacheMethod, $cacheSettings)) die ('Can not set PHPExcel cache storage setting'); // エクセルファイルを開く $objReader = PHPExcel_IOFactory::createReader('Excel2007'); $book = $objReader->load($filepath); ・・・・・・
このあたりは、PHPExcelのディスカッションボードで「Memory problem to load 」のトピックとして議論されている。
一時ファイルはスクリプトが終了したときは自動的に消えてくれるが、別のFATALエラーが起こった場合は残されるようなので、サーバにゴミを残したくないタイプの人は定期的に削除するようなバッチ運用が必要となる。
ちなみに、メモリがどの程度使用されているかを知るには、一時的にテスト環境などで大きなメモリを与えておいて、memory_get_usage()で、メモリ使用量をロギングしてみるとよい。
一時的に大きなメモリを与えられない場合は、落ちる箇所(恐らくループ内で落ちたりしているはず)に、memory_get_usage()を仕掛けてロギングすれば、およそどれくらいのメモリが必要かは実測できるだろう。
そもそも、エクセルが巨大すぎて対応できない場合
これはツライ・・というか設計が・・という話しだろう。この場合は、
- Excelを小さく分ける
- CSVに処理を変更し1行ずつロードするようなロジックに変える
- CLIで処理することでWEBサービスのメモリ管理と切り離す
などのアプローチになるのではないかと思う。
コメント
トラックバックは利用できません。
コメント (0)
この記事へのコメントはありません。