サーバのデータをExcel形式で出力したいというニーズがよくある。基本CSVでもいいのだが、基本的に数値であっても文字列として取り扱いため、CSVだと、頭の0が取れてしまうとか、"1-1"などの文字列が勝手に日付になったりという問題があるので、Excelのセルデータ型を文字列にしておいて、値をセットして出力するというのが楽でよい。
しかし、今まで使っていた、Perlだと、Excel::Writer::XLSXは、速度はそこそこだが、既存のファイルを読み込んで編集できない(テンプレート支給のケースでNG)、PHPのPHPExcelはメモリ食い過ぎ+遅すぎ+古過ぎ、後発のPhpspreadsheetはメモリ食い過ぎ+遅すぎで使えない。100行くらいまでならなんとか使えるが、数千行とか、万を超えるデータは到底扱えない。
そこで探してみたところ、LibXL PHP Extensionというのがあった。LibXLは、C++で書かれていて高速かつメモリを消費しない。数万行のデータも瞬時に書き出せる。という頼もしい記事を見つけた。類似の記事は他にもあったが代表的なものを以下に貼り付ける。
どうやら、so(shared object)を作ってphp.iniに登録すれば使えそうということがわかり、弊社のVPS(CentOS6.10、PHP5.3.3)の環境に入れてみることにした。
以下手順。
cd /usr/local/src wget http://libxl.com/download/libxl-lin-3.8.3.tar.gz tar zxf libxl-lin-3.8.3.tar.gz cd libxl-3.8.3.0 wget https://github.com/iliaal/php_excel/archive/master.zip unzip master.zip cd php_excel-master yum install php-devel phpize ./configure --with-libxl-incdir=../include_c --with-libxl-libdir=../lib64 *1 ln -s /usr/include/libxml2/libxml /usr/include/libxml make make install
configureの --with-libxl-libdir は、64bit環境の場合はlib64、32bit環境の場合はlibとする。でないと
configure: error: excel module requires libxl >= 2.4.3
のエラーが出る。
libxmlのパスが通っていなかったので設定した。これがないと
/usr/include/php/ext/xml/expat_compat.h:36:27: error: libxml/parser.h: No such file or directory /usr/include/php/ext/xml/expat_compat.h:37:36: error: libxml/parserInternals.h: No such file or directory /usr/include/php/ext/xml/expat_compat.h:38:25: error: libxml/tree.h: No such file or directory /usr/include/php/ext/xml/expat_compat.h:39:25: error: libxml/hash.h: No such file or directory
というエラーが出る。
php.iniに以下の行を追加する。ライセンス名とキーは、ライセンスを購入していないのでこのまま。もし購入したら、メールで送られてきた内容を記述して再起動。
; 2018-10-15 LibXL PHP Extension [php_excel] extension=excel.so ;excel.license_name=LICENSE_NAME ;excel.license_key=LICENSE_KEY
Apacheを再起動。
service httpd restart
テストプログラムを試す。横300列、タテ100行に6ケタのランダムな数値を入れるという内容。0埋めをしているため、セットする文字列の冒頭に"'"をつけた。
<?php ini_set('memory_limit', '256M'); $objExcel = new ExcelBook(null, null, true); $objSheet = $objExcel->addSheet('test'); $ROWS = 100; $COLS = 300; // セルに値を入れる for ($row = 0; $row < $ROWS; $row++) { for ($col = 0; $col < $COLS; $col++) { $objSheet->write($row, $col, "'".sprintf("%06d", rand(0, 1000000)), null, ExcelFormat::AS_NUMERIC_STRING); } } header('Content-Type: application/octet-stream'); header('Content-Disposition:attachment;filename="test.xlsx"'); $objExcel->save('php://output'); ?>
アクセスして瞬時にダウンロードが始まった。ちょっと感動的。この速度は使える。
今のところ、できるとわかったもの。
- 既存のファイルを読み込んで編集・書き込み
- セルのマージ
- セルにハイパーリンクを設定する
- セルに画像を貼り付ける
できないもの
- グラフまわり
- 貼り付けた画像にハイパーリンクを設定する
- オートフィルターを設定する ※PHP7+の環境で、php7ブランチからソースをもってきて構築すれば利用可能
LibXLは有償で、1ライセンス$199ドル、無制限$1399ドルとなっている。業務で使うことを考えたら、十分ありだと思った。
*1:configure時の--with-libxl-incdirと--with-libxl-libdirのパスを修正しました(2020-03-16修正)。