设为首页 收藏本站
查看: 940|回复: 0

[经验分享] php 用csv文件导出大量数据初方案

[复制链接]

尚未签到

发表于 2017-12-30 15:44:57 | 显示全部楼层 |阅读模式
  背景:接手的项目中支持导出一批数据,全数量在50W左右。在接手的时候看代码是直接一次查询MySQL获得数据,然后用header函数直接写入csv,用户开始导出则自动下载。但是,在全导出的时候,功能出现了BUG问题。
  1.数据量大导致PHP处理脚本运行时间,超过默认限制。
  2.数据量过大,导致内存溢出,流程中止。
  初版解决方案:
  1.通过函数set_time_limit(0);       取消执行时间限制(在导出的函数入口设置,这是合理的,导出的数据量过大了)
  2.关于数据过大,内存溢出的解决办法,开始是想到了php动态变量(先由sql语句获得总记录数,然后每2W条切分,查询2w条数据存入一个变量)
[php] view plain copy  

  • $total_export_count = $db->getOne("select count(1) from ($sql) t2");

  • for ($i=0;$i<intval($total_export_count/20000)+1;$i++){
  •         $export_data = "exportdata".$i;
  •         $$export_data = $db->getAll($sql." limit ".strval($i*20000).",20000");
  •     }
  然后通过相应的代码取出变量的信息,echo到csv文件中,这种方式在本地测试的时候是通过的,但在服务器上依旧会内存溢出。
[php] view plain copy  

  • header ( "Content-type:application/vnd.ms-excel" );
  •     header ( "Content-Disposition:filename=" . iconv ( "UTF-8", "GB18030", "查询用户列表" ) . ".csv" );
  •     $out = $column_name;
  •     echo iconv ( "UTF-8", "GB18030", $out );

  •     for ($i=0;$i<intval($total_export_count/20000)+1;$i++){
  •         $dynamic_name = "exportdata".$i;
  •         foreach ( $$dynamic_name as $key => $item ) {
  •             echo iconv ( "UTF-8", "GB18030", "\n".implode(',',$item) );
  •         }

  •         // 将已经写到csv中的数据存储变量销毁,释放内存占用
  •             unset($$dynamic_name);
  •     }

  •     exit ();
  因为上面的方法在服务器上没有通过,所以只能将分割数据量的操作放到写文件的流程中,相比上面的思路这种会慢一些。
[php] view plain copy  

  • header ( "Content-type:application/vnd.ms-excel" );
  • header ( "Content-Disposition:filename=" . iconv ( "UTF-8", "GB18030", "查询用户列表" ) . ".csv" );
  • $out = $column_name;
  • echo iconv ( "UTF-8", "GB18030", $out );

  • $pre_count = 20000;
  • for ($i=0;$i<intval($total_export_count/$pre_count)+1;$i++){
  •     $export_data = $db->getAll($sql." limit ".strval($i*$pre_count).",{$pre_count}");
  •     foreach ( $export_data as $key => $item ) {
  •         echo iconv ( "UTF-8", "GB18030", "\n".implode(',',$item) );
  •     }

  •     // 将已经写到csv中的数据存储变量销毁,释放内存占用
  •        unset($export_data);
  • }

  • exit ();
  经测试之后是可行的,服务器上也可以导出,就是时间会慢一些,而且会是一直下载状态。
  关于这个场景整理了一些资料:
  1.csv文件的条数是好像没有限制的,可以一直写(网上的博文里面看的,没证实过)
  2.excel 2010版本以上,是可以读取100多W行数据的(验证过,新建一个excel,ctrl+下箭头  到文件末尾可以看到行数)
  理想的解决方案(没有具体实施,想的)
  1.数据分割肯定是必须的步骤,防止内存溢出。
  2.将分割后的数据写入到一个excel或者一个csv文件中,被分割了多少次,写多少个文件。这样可以防止达到文件行数的最大限制。
  3.将2中写的文件进行压缩处理,压缩成一个压缩包,然后进行自动下载。
  补充:
  在上面的方案正式运行的时候发现导出的数据,总是比查询的总记录数要少1000多条,几次看数据后发现有些数据并没有换行,而是写到上一行去了。在有了这个觉悟后,重新看了遍之前转别人的帖子,发现还是用fputcsv()函数比较靠谱,传入一个数组,作为一行的数据,由该函数自己去写换行和控列。感觉有些时候还是不要偷懒的好啊,虽然自己写","完成列分割,写"\n"完成空格,貌似是可行的,但是对于一些数据,并不一定能控制的好。
  修改后的导出代码:
[php] view plain copy  

  • header ( "Content-type:application/vnd.ms-excel" );
  • header ( "Content-Disposition:filename=" . iconv ( "UTF-8", "GB18030", "query_user_info" ) . ".csv" );

  • // 打开PHP文件句柄,php://output 表示直接输出到浏览器
  • $fp = fopen('php://output', 'a');

  • // 将中文标题转换编码,否则乱码
  • foreach ($column_name as $i => $v) {
  •        $column_name[$i] = iconv('utf-8', 'GB18030', $v);
  •    }
  •    // 将标题名称通过fputcsv写到文件句柄
  •    fputcsv($fp, $column_name);

  • $pre_count = 10000;
  • for ($i=0;$i<intval($total_export_count/$pre_count)+1;$i++){
  •     $export_data = $db->getAll($sql." limit ".strval($i*$pre_count).",{$pre_count}");
  •     foreach ( $export_data as $item ) {
  •         $rows = array();
  •         foreach ( $item as $export_obj){
  •             $rows[] = iconv('utf-8', 'GB18030', $export_obj);
  •         }
  •         fputcsv($fp, $rows);
  •     }

  •     // 将已经写到csv中的数据存储变量销毁,释放内存占用
  •        unset($export_data);
  •        ob_flush();
  •        flush();
  • }

  • exit ();

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-429717-1-1.html 上篇帖子: PHP连接数据库:封装成类 下篇帖子: java 反序列化PHP
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表