一、背景
早期开发的系统,使用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;
}
五、结果
筛选跨年数据一次性导出,皆大欢喜。