PHPSpreadSheet メモリ不足エラーになった時の対処法

2022年8月6日土曜日

PHP

t f B! P L

スポンサーリンク

はじめに

PHPSpreadsheetで大量のデータを読み書きすると「PHP Fatal error: Allowed memory size of …」のようなエラーが出ることがある。文字通りメモリ不足である。

PHPSpreadsheetは便利であるがメモリを食うライブラリで、仕様では1セル辺り1K(64 ビット PHP では 1.6k)のメモリを使用すると書かれている。そのため、例えば「80,000行 × 12列」の帳票を扱う場合は 約960MBのメモリを食うことになる。正直、1回のリクエストで 960MBを超えるメモリ使用量を許容するハイスペックサーバーを用意するのは簡単ではない。

この記事では、PHPSpreadsheetでメモリ使用率を抑える方法について、いくつか紹介する。

複数のファイルを読み書きする場合

複数のファイル・シートを読み込むような場合、disconnectWorksheets および disconnectCells で明示的にメモリに保持されたキャッシュをクリアすることで、メモリ使用率を抑えることが可能である。逆にこの関数でキャッシュをクリアせずに、複数のファイル・シートの読み込みを続けた場合、キャッシュがクリアされずにメモリを圧迫する。

$file = ;
$reader = IOFactory::createReader('Xlsx');
$reader->setReadDataOnly(TRUE);
$spreadsheet = $reader->load("./hoge.xlsx");
$sheet = $spreadsheet->getActiveSheet();

//何かしらの処理

$spreadsheet->disconnectWorksheets();

複数のシートを読み込む場合は、対象のシートの処理が終わったタイミングで disconnectCells でシートのキャッシュを解放します。

$sheet = $sprqweadsheet->getSheet(0);

//1シート目の何かしらの処理

//シートの解放
$sheet->disconnectCells();

/////////////////////////

$sheet = $sprqweadsheet->getSheet(1);

//2シート目の何かしらの処理

//全シートの解放
$spreadsheet->disconnectWorksheets();

スポンサーリンク

1シートに大量のデータを出力する場合

1シート上の大量のデータを読み書きする場合は、上で紹介した disconnectWorksheets および disconnectCells でメモリを解放することは出来ない。

対策としては、次の3つが考えられる。

対策1: php.ini でメモリ上限を増やす

php.ini の設定でメモリの上限を増やす。

memory_limit = 256M

上限を無制限にする場合は -1 を設定する。

memory_limit = -1

対策2:ini_setで一時的にメモリ割り当てを増やす

特定のプロセス(リクエスト)のみメモリの上限を上げたい場合は、ini_set 関数で一時的に変更します。

ini_set('memory_limit', '256M');

対策3:キャッシュをメモリ以外の別の場所に保存する

メモリの上限を引き上げられない場合は、ファイルや Redis などに、セルのキャッシュを保持させることで、メモリ使用率を抑えることができます。(もしろん性能は落ちます)

■ Redisに保存する

Redisキャッシュを使用するのに必要なパッケージをインストールします。

composer require cache/simple-cache-bridge cache/redis-adapter

Excel ファイルの読み書きを行う前に、次のコードのように、セルのオブジェクトを Redisにキャッシュする設定を行います。

$client = new \Redis();
$client->connect('127.0.0.1', 6379);
$pool = new \Cache\Adapter\Redis\RedisCachePool($client);
$simpleCache = new \Cache\Bridge\SimpleCache\SimpleCacheBridge($pool);

\PhpOffice\PhpSpreadsheet\Settings::setCache($simpleCache);

■ Laravelのキャッシュに保存する

Webフレームワークに Laravelを使用している場合は、Laravelのキャッシュシステムに保存させることもできます。

use PhpOffice\PhpSpreadsheet\Settings;
use Illuminate\Cache\Repository;
use Illuminate\Support\Facades\Cache;

$cache = new Cache();
$cacheStore = $cache::getStore();
$cacheInterface = new Repository($cacheStore);
Settings::setCache($cacheInterface);

スポンサーリンク

まとめ

ここまで紹介してきたとおり、PHPSpreadsheetは結構メモリを食うライブラリで、大量のデータを読み書きする場合には注意が必要です。

あらかじめ大量データを処理すると分かっていて、それがシンプルな Excel処理であれば、
PHP_XLSXWriter
などの利用を検討してもよいでしょう。

スポンサーリンク
スポンサーリンク

このブログを検索

Profile

自分の写真
Webアプリエンジニア。 日々新しい技術を追い求めてブログでアウトプットしています。
プロフィール画像は、猫村ゆゆこ様に書いてもらいました。

仕事募集もしていたり、していなかったり。

QooQ