PHPexcle大數據量導出

最近接到一個需求,通過選擇的時間段導出對應的訂單數據到excel中, 由於數據量較大,經常會有導出500K+數據的情況。平常的導出用的PHPexcle,將數據一次性讀取到內存中再一次性寫入文件,而在面對生成超大數據量的excel文件時這顯然是會造成內存溢出的。

這時就需要循環批量寫入excle導出。

過程中可能遇到的問題:

1.超時:Maximum execution time of 30 seconds exceeded

解決:在文件開頭加上 set_time_limit(0);即php執行時間不受限制。

2.內存溢出:Allowed memory size of 134217728 bytes exhausted (tried to allocate 20480 bytes)

解決:

2.1 可在ini文件設置;設置memory_limit,將內存設置加大,

2.2 在執行代碼前加上 ini_set("memory_limit", "1024M");

2.3 每次讀取的數據量減少,增加循環的次數;

3.導出的excle數字過長展示成科學計數

解決:

3.1 在要寫入的數字數據,後面加個 “\t”;

3.2 或是數字轉換為字符串格式

上代碼(生成excle鏈接導出數據):

​public function downBigExcle()

​ ​ ​{

​ ​ ​ set_time_limit(0);

​ ​ ​ #自定義導出地址+文件名

​ ​ ​

​ ​ ​ $pathName = time().'.csv';

​ ​ ​

​ ​ ​ #計算要導出總數據條數

​ ​ ​

​ ​ ​ $totalNums = Db::table('t_excle')->count('id');

​ ​ ​

​ ​ ​ #設置表頭標題

​ ​ ​

​ ​ ​ $headerTitleArr = ['xx','xxx'];

​ ​ ​

​ ​ ​ #CSV的Excel支持GBK編碼,一定要轉換,否則亂碼

​ ​ ​

​ ​ ​ foreach ($headerTitleArr as $i => $v) {

​ ​ ​

​ ​ ​ $headerTitleArr[$i] = iconv('utf-8', 'gbk', $v);

​ ​ ​

​ ​ ​ }

​ ​ ​

​ ​ ​ #每次查詢的條數

​ ​ ​

​ ​ ​ $pageSize = 5000;

​ ​ ​

​ ​ ​ #分批導的次數

​ ​ ​

​ ​ ​ $pages = ceil($totalNums / $pageSize);

​ ​ ​

​ ​ ​ #打開文件流

​ ​ ​

​ ​ ​ $fp = fopen($pathName, 'a');

​ ​ ​

​ ​ ​ #將數據格式化為CSV格式並寫入到文件流中

​ ​ ​

​ ​ ​ fputcsv($fp, $headerTitleArr);

​ ​ ​

​ ​ ​ #主體內容

​ ​ ​

​ ​ ​ $paegNum = 0;

​ ​ ​

​ ​ ​ for ($i=0; $i

​ ​ ​

​ ​ ​ $data = Db::table('t_excle')->limit($paegNum,$pageSize)->field('*')->select();

​ ​ ​

​ ​ ​ foreach ($data as $fields) {

​ ​ ​

​ ​ ​ foreach ($fields as $key => &$v) {

​ ​ ​

​ ​ ​ // CSV的Excel支持GBK編碼,一定要轉換,否則亂碼

​ ​ ​

​ ​ ​ // 加"\t"防止數字導出時變成科學計數

​ ​ ​

​ ​ ​ $v = iconv('utf-8', 'gbk', $v."\t");

​ ​ ​

​ ​ ​ }

​ ​ ​

​ ​ ​ fputcsv($fp, $fields);

​ ​ ​

​ ​ ​ //這邊可記錄下數據的最後一次的位置,便於查看排錯定位。

​ ​ ​

​ ​ ​ }

​ ​ ​

​ ​ ​ unset($data);//釋放變量的內存

​ ​ ​

​ ​ ​ $paegNum += $pageSize;

​ ​ ​

​ ​ ​ }

​ ​ ​

​ ​ ​ fclose($fp);

​ ​ ​

​ ​ ​ #輸出下載鏈接,url為下載的域名

​ ​ ​

​ ​ ​ return $url.$pathName;

​ ​ ​

​ ​ ​ }

對於一個請求來說PHP是單線程的,大數據量的導出又是最耗時的,搞不好其他請求就阻塞了。

小數據量的話,可以直接瀏覽器輸出;

大數據量的話,可以生成excle下載鏈接;推薦使用swoole異步框架處理請求。

1.當客服在後臺系統點擊下載後,可先返回個提示或是生成一條下載記錄;

2.在後臺異步處理excle導出操作;

3.客服主動刷新頁面查看是否有下載鏈接生成。

再上代碼(生成excle直接瀏覽器輸出):

​ ​ public function downOrderData($timeStart, $timeEnd)

​ ​

​ ​{

​ ​

​ ​ set_time_limit(0);

​ ​

​ ​ $columns = [

​ ​

​ ​ '序號ID', '姓名', '電話', ......

​ ​

​ ​ ];

​ ​

​ ​ $csvFileName = '訂單數據' . $timeStart .'_'. $timeEnd . '.xlsx';

​ ​

​ ​ //設置好告訴瀏覽器要下載excel文件的headers

​ ​

​ ​ header('Content-Description: File Transfer');

​ ​

​ ​ header('Content-Type: application/vnd.ms-excel');

​ ​

​ ​ header('Content-Disposition: attachment; filename="'. $fileName .'"');

​ ​

​ ​ header('Expires: 0');

​ ​

​ ​ header('Cache-Control: must-revalidate');

​ ​

​ ​ header('Pragma: public');

​ ​

​ ​ $fp = fopen('php://output', 'a');//打開output流

​ ​

​ ​ mb_convert_variables('GBK', 'UTF-8', $columns);

​ ​

​ ​ fputcsv($fp, $columns);//將數據格式化為CSV格式並寫入到output流中

​ ​

​ ​ $accessNum = '100000'//從數據庫獲取總量,假設是十萬

​ ​

​ ​ $perSize = 1000;//每次查詢的條數

​ ​

​ ​ $pages = ceil($accessNum / $perSize);

​ ​

​ ​ $lastId = 0;

​ ​

​ ​ for($i = 1; $i <= $pages; $i++) {

​ ​

​ ​ $data = Db::table('t_excle')->limit($lastId ,$pageSize)->field('*')->select();

​ ​

​ ​ foreach($data as $value) {

​ ​

​ ​ mb_convert_variables('GBK', 'UTF-8', $value);

​ ​

​ ​ fputcsv($fp, $value);

​ ​

​ ​ $lastId = $value['id'];

​ ​

​ ​ }

​ ​

​ ​ unset($data);//釋放變量的內存

​ ​

​ ​ //刷新輸出緩衝到瀏覽器

​ ​

​ ​ ob_flush();

​ ​

​ ​ flush();//必須同時使用 ob_flush() 和flush() 函數來刷新輸出緩衝。

​ ​

​ ​ }

​ ​

​ ​ fclose($fp);

​ ​

​ ​ exit();

​ ​

​ ​}

PS:更多信息交流,請關照:倒影Amoy

活著就是為了改變世界,難道還有其他原因嗎?----喬布斯

PHPexcle大數據量導出

請輸入圖片描述


分享到:


相關文章: