閉じる
  1. Mac + Parallels で日本語入力(IME)切り替えをスムー…
閉じる
閉じる
  1. MacでVOICEROID
  2. Macの通知を制御する(Google Chromeだけオフにする対応も…
  3. 尿酸値を下げる(プリン体が原因ではなかった?)
  4. 100円でLightningケーブルを保護する vs 100均Ligh…
  5. iPhone機種変更で困ったこと(復元できない・ヘルスケア・Apple…
  6. 事業承継について(3)節税に向けて
  7. 事業承継について(2)我が社の株価はいくらか?
  8. 事業承継について(1)
  9. PHPExcelでセルの名前(ラベル)を使う
  10. MacとiPhoneでメモ帳を同期する
閉じる

リンゴと守護神とペンギンと窓

PHPExcelで大きいエクセルファイルを読み込んだときメモリ不足

PHPExcelはメモリ喰い

PHPExcelで、比較的大きなファイルをロードしたとき、メモリ不足となることがある。

Fatal error: Allowed memory size of NNNNNNNNN bytes exhausted (tried to allocate NN bytes) in path_to_PHPExcel_class

これは割と頻繁に起こる。PHPExcelはメモリ喰いなのだ。
メモリの消費は、行の多さより列の多さの方が逼迫するように思うがどうだろうか?(多パターン未検証)。

 

ファイルサイズ列数行数使用メモリ
13k23264,486,800 bytes
86k231026218,604,216 bytes
84k102326222,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サービスのメモリ管理と切り離す

などのアプローチになるのではないかと思う。

関連記事

  1. キャッシュファイルが作成できません

  2. Macでステップカウンタ(Ohcount)を使う

  3. PHPExcelでセルの名前(ラベル)を使う

  4. iOSデバイスのUDID(識別子)を調べる

おすすめ記事

  1. Mac + Parallels で日本語入力(IME)切り替えをスムーズに

コメント

  • トラックバックは利用できません。

  • コメント (0)

  1. この記事へのコメントはありません。

おすすめ記事

  1. MacでVOICEROID
  2. Macの通知を制御する(Google Chromeだけオフにする対応も)
  3. 尿酸値を下げる(プリン体が原因ではなかった?)
ページ上部へ戻る