PHPExecl匯出大量資料卡頓問題解決(Laravel實現)
阿新 • • 發佈:2020-07-14
PHPExecl匯出資料遇到複雜查詢+幾萬條資料的情況瀏覽器就容易卡死,下面分享一下解決思路和過程:
先說解決思路:
1、優化查詢、資料分批寫入檔案
2、先將表格下載到伺服器,瀏覽器輪詢伺服器,表格生成完成再下載
3、表格使用佇列匯出
上程式碼
瀏覽器端:
1、點選匯出按鈕,請求exportUser介面,將檔案加入佇列,返回唯一key
2、使用key輪詢請求getURL介面,檔案生成成功則返回檔案路徑
3、下載檔案
1 <div class="form-group col-sm-1" > 2 <a class="btn btn-block btn-success btn-bg importExcel" href="javascript:void(0);">匯出</a> 3</div> 4 5 6 <script src="{{ asset("/js/layer/layer.js")}}"></script> 7 8 $('.importExcel').on('click', function () { 9 var data = { 10 'status': $("#status_val option:selected").val(), 11 'start_time': $("input[name='start_time']").val(), 12'user_name': $("input[name='user_name']").val(), 13 'end_time': $("input[name='end_time']").val() 14 }; 15 16 $.post('/admin/user/exportUser', data, function (res) { 17 if (res.code == 200) { 18var params={ 19 'key':res.data 20 } 21 var ii = layer.load(); 22 var aaa = setInterval(() => { 23 $.post('/admin/user/getURL', params, function (reslut) { 24 var url=reslut.data.url 25 if(url){ 26 clearInterval(aaa) 27 layer.close(ii); 28 window.open(url); 29 } 30 }) 31 }, 1000) 32 } 33 }); 34 35 })
伺服器端:
1、exportUser介面
useApp\Jobs\exportList;
/** * 匯出列表 */ public function exportUser(Request $request) { $query = $request->all(); $str=str_random(10).time(); Redis::set($str,''); $arr=[ 'query'=>$query, 'type'=>1, 'key'=>$str ]; $this->dispatch(new exportList($arr)); return success($str); }
2、生成佇列任務
<?php namespace App\Jobs; use Illuminate\Bus\Queueable; use Illuminate\Queue\SerializesModels; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use App\Services\UserService; use Illuminate\Support\Facades\Log; class exportList implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; protected $data; /** * Create a new job instance. * * @return void */ public function __construct($data) { $this->data = $data; } /** * Execute the job. 處理列隊資料 * * @return void */ public function handle(UserService,$userService) { if($this->data['type']==1){ //使用者匯出 $userService->exportUser($this->data['key'],$this->data['query']);
} elseif($this->data['type']==2){ //訂單匯出 xxxxxx
}
}
}
3、分批匯出,其中getExportList方法取資料,newExportList渲染資料並匯出
/** * 批量匯出 * * @param [type] $key * @param [type] $query * @return void */ public function exportUser($key,$query) { $i = 0; $file_name = rand(1000, 9999); while (true) { $list = $this->getExportList($query, $i); //分頁取資料 if(is_null($list)){ $list=[]; } $res=$this->newExportList($key,$list, $file_name, ($i * env('EXPORT_NUM')) + 1); if (count($list) < env('EXPORT_NUM')) { return $res; } $i += 1; } }
4、getExportList方法,取資料
/** * 獲取匯出的資料 * @param $params * @return mixed */ public function getExportList($query, $page) { $list = $this->UserRepositories->getListAll($query, $page); if (!$list->isEmpty()) { $list = $list->toArray(); $data = []; foreach ($list as $key => $value) { $data[$key]['A'] = $value['user_name']; $data[$key]['B'] = "\t" . $value['phone']; $data[$key]['C'] = $value['created_at']; } return $data; } return []; }
5、newExportList 渲染資料方法
/** * 匯出資料 * @param $list */ public function newExportList($key,$list, $file_name, $total) { $arr = ['A' => '使用者名稱稱', 'B' => '手機號', 'C' => '註冊時間']; if ($total == 1) { array_unshift($list, $arr); //插入表頭 } else { $total += 1; } $title = date('Y-m-d', time()) . '_' . $file_name . '.csv'; $width = array('A' => 45, 'B' => 35, 'C' => 35);//列寬 $res=exportExcelForURL($title, $list, $width, $total); if($res){ Redis::set($key,$res); Redis::EXPIRE($key,600); } return ['code' => 200,'url'=>$res]; }
6、exportExcelForURL匯出表格方法
/** * 匯出excel 分批儲存 * @param $title * @param $list * @param $width */ function exportExcelForURL($title, $list, $width, $total = 0) { $file_name = env('EXPORT_DIR') . '/' . $title; $name=config('config.EXCEL_ROUTE'). $title; if (!file_exists($file_name)) { $myfile = fopen($file_name, "w"); fclose($myfile); } $excel = \Excel::load($file_name); $sheet = $excel->getSheet(0); foreach ($list as $row_key => $row) { foreach ($row as $key => $value) { \Log::info($key . ($row_key + $total)); $res = $sheet->setCellValue($key . ($row_key + $total), $value); } } $excel->store('csv', env('EXPORT_DIR')); if (count($list) < env('EXPORT_NUM')) { return $name; } return false; }
7、獲取下載連結
/** * 獲取下載連結 * * @param Request $request * @return void */ public function getURL(Request $request) { $key=$request->key; $data['url']=Redis::get($key); return success($data); }
8、開啟佇列(推薦配置Supervisor)
php artisan queue:work --tries=3