专栏名称: SegmentFault思否
SegmentFault (www.sf.gg)开发者社区,是中国年轻开发者喜爱的极客社区,我们为开发者提供最纯粹的技术交流和分享平台。
目录
相关文章推荐
程序员的那些事  ·  清华大学:DeepSeek + ... ·  3 天前  
程序员的那些事  ·  印度把 DeepSeek ... ·  3 天前  
程序员小灰  ·  3个令人惊艳的DeepSeek项目,诞生了! ·  2 天前  
程序猿  ·  “我真的受够了Ubuntu!” ·  4 天前  
程序员的那些事  ·  成人玩偶 + ... ·  5 天前  
51好读  ›  专栏  ›  SegmentFault思否

PHP 实时生成并下载超大数据量的 EXCEL 文件

SegmentFault思否  · 公众号  · 程序员  · 2017-12-28 08:00

正文

最近接到一个需求,通过选择的时间段导出对应的用户访问日志到excel中, 由于用户量较大,经常会有导出50万加数据的情况。而常用的PHPexcel包需要把所有数据拿到后才能生成excel, 在面对生成超大数据量的excel文件时这显然是会造成内存溢出的,所以考虑使用让PHP边写入输出流边让浏览器下载的形式来完成需求。

我们通过如下的方式写入PHP输出流

  1. $fp = fopen('php://output', 'a');

  2. fputs($fp, 'strings');

  3. ....

  4. ....

  5. fclose($fp)

php : //output 是一个可写的输出流,允许程序像操作文件一样将输出写入到输出流中,PHP会把输出流中的内容发送给web服务器并返回给发起请求的浏览器。

另外由于excel数据是从数据库里逐步读出然后写入输出流的所以需要将PHP的执行时间设长一点(默认30秒) set_time_limit ( 0 ) 不对PHP执行时间做限制。

注:以下代码只是阐明生成大数据量EXCEL的思路和步骤,并且在去掉项目业务代码后程序有语法错误不能拿来直接运行,请根据自己的需求填充对应的业务代码!

  1.    /**

  2.     * 文章访问日志

  3.     * 下载的日志文件通常很大, 所以先设置csv相关的Header头, 然后打开

  4.     * PHP output流, 渐进式的往output流中写入数据, 写到一定量后将系统缓冲冲刷到响应中

  5.     * 避免缓冲溢出

  6.     */

  7.    public function articleAccessLog($timeStart, $timeEnd)

  8.     {

  9.        set_time_limit(0);

  10.        $columns = [

  11.            '文章ID', '文章标题', ......

  12.        ];

  13.        $csvFileName = '用户日志' . $timeStart .'_'. $timeEnd . '.xlsx';

  14.        //设置好告诉浏览器要下载excel文件的headers

  15.        header('Content-Description: File Transfer');

  16.        header('Content-Type: application/vnd.ms-excel');

  17.        header('Content-Disposition: attachment; filename="'. $fileName .'"');

  18.        header('Expires: 0');

  19.        header('Cache-Control: must-revalidate');

  20.        header('Pragma: public');

  21.        $fp = fopen('php://output', 'a');//打开output流

  22.        mb_convert_variables('GBK', 'UTF-8', $columns);

  23.        fputcsv($fp, $columns);//将数据格式化为CSV格式并写入到output流中

  24.        $accessNum = '1000000'//从数据库获取总量,假设是一百万

  25.        $perSize = 1000;//每次查询的条数

  26.        $pages   = ceil($accessNum / $perSize);

  27.        $lastId  = 0;

  28.        for($i = 1; $i <= $pages; $i++) {

  29.            $accessLog = $logService->getArticleAccessLog($timeStart, $timeEnd, $lastId, $perSize);

  30.            foreach($accessLog as $access) {

  31.                $rowData = [

  32.                    ......//每一行的数据

  33.                ];

  34.                mb_convert_variables('GBK', 'UTF-8', $rowData);

  35.                fputcsv($fp, $rowData);

  36.                $lastId = $access->id;

  37.            }

  38.            unset($accessLog);//释放变量的内存

  39.            







请到「今天看啥」查看全文