最近接到一个需求,通过选择的时间段导出对应的用户访问日志到excel中, 由于用户量较大,经常会有导出50万加数据的情况。而常用的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();
}

好了, 其实很简单,就是用逐步写入输出流并发送到浏览器让浏览器去逐步下载整个文件,由于是逐步写入的无法获取文件的总体size所以就没办法通过设置header("Content-Length: $size");在下载前告诉浏览器这个文件有多大了。不过不影响整体的效果这里的核心问题是解决大文件的实时生成和下载。


更新: 说一下我数据库查询这里的思路,因为逐步写入EXCEL的数据实际上来自Mysql的分页查询,大家知道其语法是LIMIT offset, num 不过随着offset越来越大Mysql在每次分页查询时需要跳过的行数就越多,这会严重影响Mysql查询的效率(包括MongoDB这样的NoSQL也是不建议skip掉多条来取结果集),所以我采用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

原文地址:https://segmentfault.com/a/1190000011663425

PHP实时生成并下载超大数据量的EXCEL文件的更多相关文章

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

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

  2. 【Easyexcel】java导入导出超大数据量的xlsx文件 解决方法

    解决方法: 使用easyexcel解决超大数据量的导入导出xlsx文件 easyexcel最大支持行数 1048576. 官网地址: https://alibaba-easyexcel.github. ...

  3. 效率最高的Excel数据导入---(c#调用SSIS Package将数据库数据导入到Excel文件中【附源代码下载】) 转

    效率最高的Excel数据导入---(c#调用SSIS Package将数据库数据导入到Excel文件中[附源代码下载])    本文目录: (一)背景 (二)数据库数据导入到Excel的方法比较   ...

  4. 数据导出至Excel文件--好库编程网http://code1.okbase.net/codefile/SerializeHelper.cs_2012122018724_118.htm

    using System; using System.IO; using System.Data; using System.Collections; using System.Data.OleDb; ...

  5. Qt中将QTableView中的数据导出为Excel文件

    如果你在做一个报表类的程序,可能将内容导出为Excel文件是一项必须的功能.之前使用MFC的时候我就写过一个类,用于将grid中的数据导出为Excel文件.在使用了QtSql模块后,我很容易的将这个类 ...

  6. C#将数据集DataSet中的数据导出到EXCEL文件的几种方法

    using System; using System.Collections.Generic; using System.Text; using System.Data; using System.W ...

  7. Pl/sql 如何将oracle的表数据导出成excel文件?

    oracle将表数据导出成excel文件的方法 1)在SQL窗体上,查询需要导出的数据 --查询数据条件-- ; 结果视图 2)在查询结果的空白处,右键选择Copy to Excel 3) 查看导出e ...

  8. VB将MSHFlexGrid数据导出到Excel文件通用功能

    1.通用导出Excel功能. 2.将 MSHFlexGrid数据导出到Excel文件通用功能. 3.具体代码如下: '将下列代码保存到一模块文件中,调用方法:Export fgrid1,cd1 Pub ...

  9. 用node.js写一个简单爬虫,并将数据导出为 excel 文件

    引子 最近折腾node,最开始像无头苍蝇一样到处找资料,然而多数没什么卵用,都在瞎比比.在一阵瞎搞后,我来分享一下初步学习node的三个过程: 1 撸一遍NODE入门,对其有个基本的了解: 2 撸一遍 ...

随机推荐

  1. java 后台实现ajax post跨域请求传递json格式数据获取json数据问题

    参考大神:http://blog.csdn.net/chunqiuwei/article/details/19924821 java后台: public String ajaxProxy(Intege ...

  2. inkscape

    最近的论文需要把latex公式放到图里,于是想起了装了之后一直没怎么用的inkscape.因为visio貌似没法直接贴latex格式的公式. 找了几个用法: http://blog.sciencene ...

  3. 在 Microsoft Word 文档 中粘贴代码实现语法高亮的方法

    1.下载notepad++. 2.将代码粘贴进notepad++,或者直接用notepad++打开. 3.点击顶栏 ===> 插件 ===> NppExport ===> cope ...

  4. angular.js学习-ng-grid

    ng-grid是基于AngularJS和JQuery的富表格控件,由AngularUI Team领衔开发,到目前为止已有2354次Commit,1076个Fork.  AngualrUI:http:/ ...

  5. swift语言点评十八-异常与错误

    1.错误类型与枚举的结合 enum VendingMachineError: Error { case invalidSelection case insufficientFunds(coinsNee ...

  6. SpringCloud学习笔记(8)----Spring Cloud Netflix之负载均衡-Ribbon的负载均衡的策略

    一. 内置 负载均衡策略的介绍的 IRule的实现类 2. 通过代码实现负载均衡 在第六节Riddom的使用的工程中,随机策略配置类 package com.wangx.cloud.springclo ...

  7. 启动tomcat运行maven工程报错:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:2.3.2:

    控制台报错信息:

  8. win10 无法访问XP 共享目录原因

    win10 无法访问XP 共享目录原因     *现象:  在地址栏中输入\\192.168.100.5 (XP文件服务器),出现:.....找不到网络路径, 此连接尚未还原.             ...

  9. moment.js获取本周本月本年的开始日期和结束日期

    //获取本日 const startDate = moment().format('YYYY-MM-DD'); const startDate = moment().format('YYYY-MM-D ...

  10. 紫书 习题 8-16 UVa 1618 (中途相遇法)

    暴力n的四次方, 然而可以用中途相遇法的思想, 分左边两个数和右边两个数来判断, 最后合起来判断. 一边是n平方logn, 合起来是n平方logn(枚举n平方, 二分logn) (1)两种比较方式是相 ...