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", "\n".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", "\n".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()函數比較靠譜,傳入一個數組,作為一行的數據,由該函數自己去寫換行和控列。感覺有些時候還是不要偷懶的好啊,雖然自己寫","完成列分割,寫"\n"完成空格,貌似是可行的,但是對於一些數據,並不一定能控制的好。

 

修改后的導出代碼:

[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 ();  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM