ホームページ  >  記事  >  バックエンド開発  >  PHP は、非常に大量のデータを含む EXCEL ファイルをリアルタイムで生成およびダウンロードします。

PHP は、非常に大量のデータを含む EXCEL ファイルをリアルタイムで生成およびダウンロードします。

藏色散人
藏色散人転載
2019-08-26 14:20:443927ブラウズ

最近、選択した期間の対応するユーザー アクセス ログを 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(&#39;GBK&#39;, &#39;UTF-8&#39;, $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` >= &#39;time range start&#39; 
AND `created_at` <= &#39;time range end&#39; 
AND  `id` < LastId 
ORDER BY `id` DESC 
LIMIT num

以上がPHP は、非常に大量のデータを含む EXCEL ファイルをリアルタイムで生成およびダウンロードします。の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はsegmentfault.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。