一、背景
早期开发的系统,使用laravel框架,版本V5.4,项目经理导出 3 年的数据,由于数据量较大,浏览器卡死。一次性无法导出,某位程序员告知按月去导出,之后在拼凑,这。。搁谁受的了,我担心投诉,加个班优化下。
二、优化方案
- 导出数据的Sql,对应创建索引,提高查询速度
- 查询结果集使用 chunk() 方法拆分较小集合
- 使用box/spout扩展进行导出
三、box/spout扩展安装
由于服务器 PHP 的版本比较老,5.6,box/spout 版本只能安装 v2.7.3,安装流程:
3.1 安装
composer require box/spout:v2.7.3
3.2 使用导出功能
3.2.1 控制器中引入使用到的方法:
use Box\Spout\Common\Type;
use Box\Spout\Writer\WriterFactory;
3.2.2 主要代码示例:
// 实例化类,传递参数:Type::CSV 代表导出 csv 文件,支持 3 种格式
$writer = WriterFactory::create(Type::CSV);// 浏览器下载方式
// 注意:这里的openToBrowser方法,扩展包源码只传递 1 个 参数,有修改源码,参考四。。
$filename = '文件名称.csv';
$aHeader = ['Content-Description: File Transfer','Content-Disposition: attachment; filename=' . $filename,'Expires: 0','Cache-Control: must-revalidate','Pragma: public',
];
$writer->openToBrowser($filename, $aHeader);// 支持文件存储
// 代码:$writer->openToFile("存储路径"); // addRow,添加单行 - 表头
$title = ['姓名', '年龄'];
$writer->addRow($title);// addRow,添加单行 - 示例内容
$content = ['张大胆', 18];
$writer->addRow($content);// [重要],添加多行内容,使用addRows。查询数据库之后的代码循环拼接内容,示例:
$aDates = [];
$aDates[] = [['李胆大', 20],['王老五', 25],['钱老三', 33],
];
// 假设 - 循环数据库查询结果集,拼多行内容。
foreach ($dbData as $data) $aDatas[] = $data;
}// 添加多行
$writer->addRows($aDatas);
// 导出完毕后,关闭Writer对象:
$writer->close();
四、遇到的问题及修改源码包
本地windows环境导出正常,正式 centos 环境导出没有文件名,只有一个csv后缀文件。
怀疑是header的问题,修改源代码,自定义传递 header
修改源码位置:
vendor\box\spout\src\Spout\Writer\WriterInterface接口,30行,增加了 , $headers = []参数
public function openToBrowser($outputFileName, $headers = []);
vendor\box\spout\src\Spout\Writer\AbstractWriter类,134行,增加了 , $headers = []参数
public function openToBrowser($outputFileName, $headers = []){$this->outputFilePath = $this->globalFunctionsHelper->basename($outputFileName);$this->filePointer = $this->globalFunctionsHelper->fopen('php://output', 'w');$this->throwIfFilePointerIsNotAvailable();// Clear any previous output (otherwise the generated file will be corrupted)// @see https://github.com/box/spout/issues/241$this->globalFunctionsHelper->ob_end_clean();// Set headers$this->globalFunctionsHelper->header('Content-Type: ' . static::$headerContentType);/** When forcing the download of a file over SSL,IE8 and lower browsers fail* if the Cache-Control and Pragma headers are not set.** @see http://support.microsoft.com/KB/323308* @see https://github.com/liuggio/ExcelBundle/issues/45*//** 核心修改代码位置*/foreach ($headers as $header){$this->globalFunctionsHelper->header($header);}$this->openWriter();$this->isWriterOpened = true;return $this;}
五、结果
筛选跨年数据一次性导出,皆大欢喜。