追記:この記事が意図に違った紹介のされ方をしてしまっているようなので、念のため注意喚起させていただきます。「CSVを生成するときにはfputcsv()を使う」ということを紹介している記事ではありません。教科書通りの方法では、現実にはサーバ障害を発生させかねない危険性を説明した記事です。この記事では、結論に至るまで順を追って記載しています。結論は最後に書いてあります。単純にCSVを作ることは簡単です。単純な方法で実装すると、次のような問題でやられたりします。CSVダウンロードボタンを押してしばらくしたら画面が真っ白にCSVがダウンロードされている間サーバに誰もアクセスできないCSVダウンロードボタンを押した瞬間にサーバがハングする要するに、行数が少なければどのような方法でも変わりありませんが、単純な方法ではサーバリソース上の問題が出る場合がある、ということです。だいたいブラウザ画面からCSVをダウンロードするということは、管理画面的なものがあって、条件検索してデータを絞り込んで出力するということです。これが年月日単位で検索するデータであれば、日次バッチで生成しておいたものを合体してダウンロードさせるというのが鉄板ですね。いう検索の場合は除外して、いわゆるマスタデータに対する検索のようなものを想定することとします。対象レコード数が10万、テーブルのカラム数が50、検索条件対象カラム数が30として、考えてみましょう。影響など)は横に置きます。DBの検索対象テーブルにINDEXが正しく設定されているか否かというのも当然大きい要素ではありますが、これもここでは除外します。想定されるいくつかの方法と、その問題点をそれぞれ追ってみます。生成方法1.変数に出力テキストを追加していく方式たとえば入門書やブログなどで紹介されている最もシンプルな方法は次のようなものだと思います。$buf;これ単体でダメだという話ではありません。$dataが100件ならいいですが、数千件になるようであればCPU使用率を振り切るかメモリを使い切って落ちたりしだします。上記コード例は絶対にマネしてはいけない、ということになりますね。特定の入門書やブログをdisっているわけではありません。実装として正しくないだけで概念の説明としては正しいと思うので。2.いったんサーバ内にファイル保存する方式これはどういうことかというと、サーバローカルにfopenしたファイルに対して、1行づつCSVを追記していき、全部終わったところでreadfileなりして返却する、という方式です。filename=hoge.csv’);readfile($tmp_file);unlink($tmp_file);この方式ではメモリは抑えられますが、ファイルI/Oが10万回発生するということになると、スペック低めのサーバではCPU使用率が振り切れます。ストリームラッパーを使用して、データをメモリに保存していくという方法です。一定以上の容量に達した場合自動的にテンポラリファイルに逃がされる、という点です。すれば、「5MBを超えるまではメモリ上に保持」ということになります。方が明らかに安全ですね。メモリ上に保持されている限りは問題がありませんが、テンポラリファイル扱いになった瞬間からファイルI/Oが大量に発生しCPU使用率が激増することになるので注意が必要です。直接ブラウザに返却するのが最短の手段です。‘,’,'”‘);}fclose($fp);この方式の場合、出力がHTTPレスポンスそのものになるため、ブラウザに対してレスポンスデータを「ダウンロード」として開始するよう指定しなければなりません。header()の位置は重要ですね。Content-lengthヘッダを送出することができないため、ブラウザ側の「ダウンロード残り時間」や「ダウンロードゲージ」は表示されなくなります。この点は一応注意が必要です。文字コードここまでの説明で意図的に無視してきた要素がもう一つあります。Excelが想定している日本語CSVのデフォルトエンコードは”Shift_JIS”である」(正確にはShift_JISではありませんが)という、おなじみの件です。上記に挙げたコードではおそらくUTF-8のCSVができ上がってしまうでしょう。「どうせExcelで開いた時コンバートするんだから別にいいじゃん」と言える世界の住人とはここでお別れです。・fputcsvの引数に指定する前に配列要素に対して再帰的に文字コード変換をかける・fputcsvの使用をあきらめていったん変換した文字列をfwriteするという判断になるかと思います。ストリームフィルタを使えば、fputcsvをあきらめる必要はありません。stream_filter_appendhttp://www.php.net/manual/ja/function.stream-filter-append.php先ほどのコードで言うと、fopenの後、出力が始まる前に追加します。‘,’,'”‘);}fclose($fp);stream_get_filters()のの結果に”convert.iconv.*”が存在しない場合はもちろん動作しません。登録することになりますが、文字コード変換だけであればこれで十分です。#もちろん、UTF-8からShift_JISへの変換にともなう問題(「ハシゴ高」など)はあります。した処置がどれだけサーバリソースを抑えられるか、というのは、もちろん環境に依存するところは多々あります。経験上も、あまり真っ先に疑う個所ではなかったりすると思うので、ハマりポイントとして挙げておくと何かの役に立つかもしれないと思う次第です。技術担当取締役を中心に技術者が書いております。プログラミング・サーバー/クラウド技術などについての小ネタから、システム化にまつわるあれこれや、プログラマー教育、そこから発生する様々な考え方などまでご紹介。