最近、選択した期間の対応するユーザー アクセス ログを Excel にエクスポートするリクエストを受け取りました。ユーザー数が多いため、500,000 を超えるデータがエクスポートされることもよくあります。
一般的に使用される PHPexcel パッケージは、Excel を生成する前にすべてのデータを取得する必要があります。これにより、大量のデータを含む Excel ファイルを生成する場合に明らかにメモリ オーバーフローが発生するため、PHP を使用して次のように書き込むことを検討してください。出力ストリームにより、ブラウザはダウンロードの形式でリクエストを完了できます。
PHP 出力ストリームは次の方法で記述します。
$fp = fopen('php://output', 'a'); fputs($fp, 'strings'); .... .... fclose($fp)
php://output は書き込み可能な出力ストリームであり、プログラムがファイルのように出力ストリームに出力を書き込むことができます。PHP出力ストリームのコンテンツを Web サーバーに送信し、リクエストを開始したブラウザに返します。
さらに、Excel データはデータベースから徐々に読み取られ、出力ストリームに書き込まれるため、 PHP の実行時間を長く設定する必要があります (デフォルトは 30 秒) set_time_limit(0) は PHP の実行時間を制限しません。
注:
次のコードは、大量のデータを含む EXCEL を生成するためのアイデアと手順を示しているだけです。プロジェクトのビジネス コードを削除すると、プログラムに構文エラーが発生します。直接実行することはできません。必要に応じて、対応するビジネス コードを入力してください。
/** * 文章访问日志 * 下载的日志文件通常很大, 所以先设置csv相关的Header头, 然后打开 * PHP output流, 渐进式的往output流中写入数据, 写到一定量后将系统缓冲冲刷到响应中 * 避免缓冲溢出 */ public function articleAccessLog($timeStart, $timeEnd) { set_time_limit(0); $columns = [ '文章ID', '文章标题', ...... ]; $csvFileName = '用户日志' . $timeStart .'_'. $timeEnd . '.xlsx'; //设置好告诉浏览器要下载excel文件的headers header('Content-Description: File Transfer'); header('Content-Type: application/vnd.ms-excel'); header('Content-Disposition: attachment; filename="'. $fileName .'"'); header('Expires: 0'); header('Cache-Control: must-revalidate'); header('Pragma: public'); $fp = fopen('php://output', 'a');//打开output流 mb_convert_variables('GBK', 'UTF-8', $columns); fputcsv($fp, $columns);//将数据格式化为CSV格式并写入到output流中 $accessNum = '1000000'//从数据库获取总量,假设是一百万 $perSize = 1000;//每次查询的条数 $pages = ceil($accessNum / $perSize); $lastId = 0; for($i = 1; $i <= $pages; $i++) { $accessLog = $logService->getArticleAccessLog($timeStart, $timeEnd, $lastId, $perSize); foreach($accessLog as $access) { $rowData = [ ......//每一行的数据 ]; mb_convert_variables('GBK', 'UTF-8', $rowData); fputcsv($fp, $rowData); $lastId = $access->id; } unset($accessLog);//释放变量的内存 //刷新输出缓冲到浏览器 ob_flush(); flush();//必须同时使用 ob_flush() 和flush() 函数来刷新输出缓冲。 } fclose($fp); exit(); }
わかりました。実際は非常に簡単です。出力ストリームをステップごとに記述してブラウザに送信し、ブラウザにファイル全体をステップごとにダウンロードさせることです。ステップごとに記述されているため、ファイル全体のサイズを取得できないため、ヘッダー("Content-Length: $size"); を設定することで、ダウンロードする前にファイルのサイズをブラウザーに伝える方法があります。ただし、全体的な効果には影響せず、ここでの中心的な問題は、大きなファイルのリアルタイム生成とダウンロードを解決することです。
更新: ここで、データベース クエリについての私の考えについて話させてください。EXCEL に徐々に書き込まれるデータは、実際には Mysql のページング クエリから来ているためです。構文が LIMIT であることは誰もが知っています。 offset、num ですが、オフセットが大きいほど、MySQL が各ページング クエリでスキップする必要がある行が多くなり、MySQL クエリの効率に重大な影響を及ぼします (MongoDB などの NoSQL を含むため、結果セット)なので、LastId を使用してページング クエリを実行します。
次のステートメントと同様:
SELECT columns FROM `table_name` WHERE `created_at` >= 'time range start' AND `created_at` <= 'time range end' AND `id` < LastId ORDER BY `id` DESC LIMIT num
以上がPHP は、非常に大量のデータを含む EXCEL ファイルをリアルタイムで生成およびダウンロードします。の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。