zoukankan      html  css  js  c++  java
  • php 用csv文件导出大量数据初方案

    背景:接手的项目中支持导出一批数据,全数量在50W左右。在接手的时候看代码是直接一次查询MySQL获得数据,然后用header函数直接写入csv,用户开始导出则自动下载。但是,在全导出的时候,功能出现了BUG问题。

    1.数据量大导致PHP处理脚本运行时间,超过默认限制。

    2.数据量过大,导致内存溢出,流程中止。

    初版解决方案:

    1.通过函数set_time_limit(0);       取消执行时间限制(在导出的函数入口设置,这是合理的,导出的数据量过大了)

    2.关于数据过大,内存溢出的解决办法,开始是想到了php动态变量(先由sql语句获得总记录数,然后每2W条切分,查询2w条数据存入一个变量)

    [php] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. $total_export_count = $db->getOne("select count(1) from ($sql) t2");  
    2.       
    3. for ($i=0;$i<intval($total_export_count/20000)+1;$i++){  
    4.         $export_data = "exportdata".$i;  
    5.         $$export_data = $db->getAll($sql." limit ".strval($i*20000).",20000");  
    6.     }  

    然后通过相应的代码取出变量的信息,echo到csv文件中,这种方式在本地测试的时候是通过的,但在服务器上依旧会内存溢出。

    [php] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. header ( "Content-type:application/vnd.ms-excel" );  
    2.     header ( "Content-Disposition:filename=" . iconv ( "UTF-8", "GB18030", "查询用户列表" ) . ".csv" );     
    3.     $out = $column_name;  
    4.     echo iconv ( "UTF-8", "GB18030", $out );  
    5.       
    6.     for ($i=0;$i<intval($total_export_count/20000)+1;$i++){  
    7.         $dynamic_name = "exportdata".$i;  
    8.         foreach ( $$dynamic_name as $key => $item ) {  
    9.             echo iconv ( "UTF-8", "GB18030", " ".implode(',',$item) );  
    10.         }  
    11.           
    12.         // 将已经写到csv中的数据存储变量销毁,释放内存占用  
    13.             unset($$dynamic_name);  
    14.     }  
    15.       
    16.     exit ();  

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

    [php] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. header ( "Content-type:application/vnd.ms-excel" );  
    2. header ( "Content-Disposition:filename=" . iconv ( "UTF-8", "GB18030", "查询用户列表" ) . ".csv" );     
    3. $out = $column_name;  
    4. echo iconv ( "UTF-8", "GB18030", $out );  
    5.   
    6. $pre_count = 20000;  
    7. for ($i=0;$i<intval($total_export_count/$pre_count)+1;$i++){  
    8.     $export_data = $db->getAll($sql." limit ".strval($i*$pre_count).",{$pre_count}");  
    9.     foreach ( $export_data as $key => $item ) {  
    10.         echo iconv ( "UTF-8", "GB18030", " ".implode(',',$item) );  
    11.     }  
    12.       
    13.     // 将已经写到csv中的数据存储变量销毁,释放内存占用  
    14.        unset($export_data);  
    15. }  
    16.   
    17. exit ();  

    经测试之后是可行的,服务器上也可以导出,就是时间会慢一些,而且会是一直下载状态。

    关于这个场景整理了一些资料:

    1.csv文件的条数是好像没有限制的,可以一直写(网上的博文里面看的,没证实过)

    2.excel 2010版本以上,是可以读取100多W行数据的(验证过,新建一个excel,ctrl+下箭头  到文件末尾可以看到行数)

    理想的解决方案(没有具体实施,想的)

    1.数据分割肯定是必须的步骤,防止内存溢出。

    2.将分割后的数据写入到一个excel或者一个csv文件中,被分割了多少次,写多少个文件。这样可以防止达到文件行数的最大限制。

    3.将2中写的文件进行压缩处理,压缩成一个压缩包,然后进行自动下载。

    补充:

    在上面的方案正式运行的时候发现导出的数据,总是比查询的总记录数要少1000多条,几次看数据后发现有些数据并没有换行,而是写到上一行去了。在有了这个觉悟后,重新看了遍之前转别人的帖子,发现还是用fputcsv()函数比较靠谱,传入一个数组,作为一行的数据,由该函数自己去写换行和控列。感觉有些时候还是不要偷懒的好啊,虽然自己写","完成列分割,写" "完成空格,貌似是可行的,但是对于一些数据,并不一定能控制的好。

    修改后的导出代码:

    [php] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. header ( "Content-type:application/vnd.ms-excel" );  
    2. header ( "Content-Disposition:filename=" . iconv ( "UTF-8", "GB18030", "query_user_info" ) . ".csv" );  
    3.   
    4. // 打开PHP文件句柄,php://output 表示直接输出到浏览器  
    5. $fp = fopen('php://output', 'a');   
    6.   
    7. // 将中文标题转换编码,否则乱码  
    8. foreach ($column_name as $i => $v) {    
    9.        $column_name[$i] = iconv('utf-8', 'GB18030', $v);    
    10.    }  
    11.    // 将标题名称通过fputcsv写到文件句柄    
    12.    fputcsv($fp, $column_name);  
    13.   
    14. $pre_count = 10000;  
    15. for ($i=0;$i<intval($total_export_count/$pre_count)+1;$i++){  
    16.     $export_data = $db->getAll($sql." limit ".strval($i*$pre_count).",{$pre_count}");  
    17.     foreach ( $export_data as $item ) {  
    18.         $rows = array();  
    19.         foreach ( $item as $export_obj){  
    20.             $rows[] = iconv('utf-8', 'GB18030', $export_obj);  
    21.         }  
    22.         fputcsv($fp, $rows);  
    23.     }  
    24.       
    25.     // 将已经写到csv中的数据存储变量销毁,释放内存占用  
    26.        unset($export_data);  
    27.        ob_flush();  
    28.        flush();  
    29. }  
    30.   
    31. exit ();  
  • 相关阅读:
    layoutSubviews, setNeedsLayout, layoutIfNeeded
    UIViewController 专题
    UIBarButtonItem
    ios method swizzling
    Method Swizzling
    ios逆向工程
    IOS中通知中心NSNotificationCenter应用总结
    Swift 进阶
    swift 2.0 语法 字符串
    C++Builder中MessageBox的基本用法
  • 原文地址:https://www.cnblogs.com/houdj/p/6492009.html
Copyright © 2011-2022 走看看