先说一下一般我们在执行大数据操作时,遇到最常见的问题就是:
1、执行超时 参考这里
2、内存溢出 参考这里
看了这两个之后再来看这个方案,实现思路大概是:
- 先利用set_time_limit(0)不限制执行时间
- 将要查询的数据分段查询,每次查询创建一个csv临时文件,每次清空缓冲区
- 数据全部写完之后将所有csv文件合并成一个csv文件
- 合并之后利用zip扩展进行打包下载(如果没有安装此扩展,请先去安装扩展)
- 最后将所有生成的文件删除
/** * 百万级数据导出 */ public function bigExport(){ //不限制执行时间,以防超时 set_time_limit(0); //文件名 $xlsName = '名字'.date('Ymd His'); //统计总行数 $sqlCount = 0; //表头 $xlsCell = ['列1','列2']; //对应表头的字段 $fields = 'field1,field2'; //模型,主要使用获取器进行字段值转换 $orderExportModel = new OrderExport(); //统计总行数 $sqlCount = $orderExportModel->count(); //每次取多少条 $sqlLimit = 2000;//每次只从数据库取2000条 // buffer计数器 $cnt = 0; $fileNameArr = array(); //分段执行,以免内存写满 for ($i = 0; $i < ceil($sqlCount / $sqlLimit); $i++) { $fp = fopen($xlsName . '_' . $i . '.csv', 'w'); //生成临时文件 $fileNameArr[] = $xlsName . '_' . $i . '.csv';//将临时文件保存起来 //第一次执行时将表头写入 if($i == 0){ fputcsv($fp, $xlsCell); } //查询出数据 $xlsData = $orderExportModel ->field($fields) ->limit($i * $sqlLimit,$sqlLimit) ->select(); //转换为数组 $xlsData = collection($xlsData)->toArray(); foreach ($xlsData as $k=>$v) { $cnt++; //执行下一次循环之前清空缓冲区 if ($sqlLimit == $cnt) { ob_flush(); $cnt = 0; } //每行写入到临时文件 fputcsv($fp, $v); } fclose($fp); //每生成一个文件关闭 } //将所有临时文件合并成一个 foreach ($fileNameArr as $file){ //如果是文件,提出文件内容,写入目标文件 if(is_file($file)){ $fileName = $file; //打开临时文件 $handle1 = fopen($fileName,'r'); //读取临时文件 if($str = fread($handle1,filesize($fileName))){ //关闭临时文件 fclose($handle1); //打开或创建要合并成的文件,往末尾插入的方式添加内容并保存 $handle2 = fopen($xlsName.'.csv','a+'); //写入内容 if(fwrite($handle2, $str)){ //关闭合并的文件,避免浪费资源 fclose($handle2); } } } } //将文件压缩,避免文件太大,下载慢 $zip = new \ZipArchive(); $filename = $xlsName . ".zip"; $zip->open($filename, \ZipArchive::CREATE); //打开压缩包 $zip->addFile($xlsName.'.csv', basename($xlsName.'.csv')); //向压缩包中添加文件 $zip->close(); //关闭压缩包 foreach ($fileNameArr as $file) { unlink($file); //删除csv临时文件 } //输出压缩文件提供下载 header("Cache-Control: max-age=0"); header("Content-Description: File Transfer"); header('Content-disposition: attachment; filename=' . basename($filename)); // 文件名 header("Content-Type: application/zip"); // zip格式的 header("Content-Transfer-Encoding: binary"); // header('Content-Length: ' . filesize($filename)); // @readfile($filename);//输出文件; unlink($filename); //删除压缩包临时文件 unlink($xlsName.'.csv'); //删除合并的临时文件 }
————————————————
版权声明:本文为CSDN博主「LiDong99」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/LiDong99/article/details/103386234