我所經曆的大檔案資料匯出(後台執行,自動產生),資料匯出自動產生
一、前言
記錄一下以前做的後台excel格式匯出統計資訊的功能,也是最近同事問到了相關東西,一時之間竟忘了具體的細節,因此記錄一下;
大家知道,excel匯出資料的功能,後台幾乎是必須功能,一般都是點擊後,組建檔案然後自動下載,
如果是資料量小的話,一下子便可請求完成,從而下載到本地;
但是,如果資料量特別大的時候,頁面就必須一直在等待,直到寫入excel成功,
這樣便影響了後台使用者無法操作其他頁面,為此,對excel匯出做了以下功能最佳化:
二、產生excel檔案
產生excel檔案的方法有很多,暫不一一記錄,只是記錄本次的方法;
這裡用到了table的html格式,以及相應的excel的聲明
(隱約記得其他的方法用office07開啟的時候好像是亂碼,後面嘗試用csv格式檔案,可還是亂碼,所以用了table的形式)
檔案的開頭:
1 $struserdata = <<<Eof 2 3 xmlns:x="urn:schemas-microsoft-com:office:excel" 4 xmlns="http://www.w3.org/TR/REC-html40"> 5 6 PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 7 8 9 10 12 13 14 15 16 17View Code
檔案的結尾:
1 $struserdata = <<<Eof2
3 4 5 6 Eof;View Code
當然,檔案中間就是一些tr td 標籤了。
三、讓程式在後台執行
情境:
使用者點擊 產生excel後,跳轉到下載頁面,程式在後台執行,使用者可不必等待產生完成,可執行其他動作;
下載頁面可看到檔案產生的進度以及是否可下載狀態
思路:
點擊 產生excel,顯示下載頁面 ---> show_download方法
產生excel ---> create_excel 方法
show_download方法中調用 create_excel方法,而show_download 方法中,自己用了一下命令列執行程式的方式,
利用php命令列的方式,把參數傳遞給 create_excel方法
1 // $cmd = "/usr/bin/php /home/xxx/xxx.php " . $strjoin . " >/dev/null & ";2 // $a=exec($cmd, $out, $returndata);3 4 5 $command = "/usr/bin/php ".STATISTIC_EXPORT_SCRIPT_DIR."xxx.php " . "'" .$strjoin ."'". " " . $uid . " ". $action ." & ";6 $process = proc_open($command, array(),$pipes); 7 $var = proc_get_status($process); 8 proc_close($process);9 $pid = intval($var['pid'])+1;
而在create_excel方法中:
需填寫以下代碼:
1 set_time_limit(0); //取消指令碼已耗用時間的逾時上限2 3 ignore_user_abort(TRUE); //後台運行,不受使用者關閉瀏覽器的影響
調用相關的api得到資料:
1 $statistic = call_user_func(array('shellscript','get_result'),$url,$params);2 if(!is_object($statistic) || !isset($statistic->data->items)){3 usleep(400000);//停止400毫秒4 $statistic = call_user_func(array('shellscript','get_result'),$url,$params);5 }
四、顯示檔案產生進度
但是怎麼顯示相應的檔案產生進度呢,怎麼知道檔案到底產生好了沒有呢?
這裡,我用到的方法是,在寫入資料檔案的時候data.xsl,每個資料檔案都產生一個對應的檔案進度檔案,暫且稱為flag_data.xsl;
思路:
查看檔案的進度方法:
1 public function execscript_process(){ 2 $this->load->library('smarty'); 3 $file_arr_str = array(); 4 $file_arr_process = array(); 5 $file_arr_name = array(); 6 $file_arr = array(); 7 $refresh_flag = 'yes'; 8 $uid = $_REQUEST['uid']; 9 $url_dir = STATISTIC_EXPORT_FILE_DIR.$uid .'/';//@todo10 if(!is_dir($url_dir)){11 @mkdir($url_dir,0777);12 }13 $files = scandir($url_dir);14 15 if(!empty($files)){16 foreach ($files as $key => $value) {17 if($value!='.' && $value!='..'){18 if(substr($value, 0 , 5)=="flag_"){19 $file_size = filesize($url_dir . $value);20 if(!empty($file_size)){21 $fhandle = fopen($url_dir . $value, 'rb+');22 fseek($fhandle, -1, SEEK_END);23 $fstr = '';24 while(($c = fgetc($fhandle)) !== false) {25 if($c == "\n" && $fstr) break;26 $fstr = $c . $fstr;27 fseek($fhandle, -2, SEEK_CUR);28 }29 fclose($fhandle);30 $fstr = trim($fstr);31 $fstr_arr_str = explode(',', $fstr);32 $file_arr_process[] = 100 * number_format($fstr_arr_str[0]/$fstr_arr_str[1],2).'%';33 $file_arr_name[] = substr($value,5);34 }35 }36 }37 }38 39 foreach ($file_arr_process as $key => $value) {40 if($value != '100%'){41 $refresh_flag = 'no';42 break;43 }44 }45 }46 47 $file_arr = array(48 'process' => $file_arr_process,49 'name' => $file_arr_name,50 'refresh_flag' => $refresh_flag51 );52 $file_arr_json = json_encode($file_arr);53 echo $file_arr_json;54 }View Code
五、下載檔案
檔案的下載就好說了,既然已經都產生成功,下載的方法如下:
1 public function execscript_download(){ 2 $filename = $_REQUEST['filename']; 3 $uid = $_REQUEST['uid']; 4 $file_dir = STATISTIC_EXPORT_FILE_DIR.$uid.'/'.$filename; 5 if (!file_exists($file_dir)){ 6 header("Content-type: text/html; charset=utf-8"); 7 echo "File not found!"; 8 exit; 9 } else {10 ini_set("memory_limit","500M"); 11 header('Content-Description: File Transfer');12 header('Content-Type: application/octet-stream');13 header('Content-Disposition: attachment; filename='.basename($file_dir));14 header('Content-Transfer-Encoding: binary');15 header('Expires: ' . gmdate('D, d M Y H:i:s') . ' GMT');16 header('Cache-Control: must-revalidate,post-check=0, pre-check=0');17 header('Pragma: public');18 header('Content-Length: ' . filesize($file_dir));19 readfile($file_dir);20 }21 22 }
六、上線後出現的問題
本地本來已經測試完畢,可上線後,卻出現了奇怪的問題;
現象描述:
當在後台點擊組建檔案,跳轉到下載頁的時候,因為下載頁是顯示檔案進度的頁面,
竟然出現有時候有剛剛點擊的檔案進度,有時候沒有,就感覺沒有產生相應的檔案一樣;
解決方案:
因為資料檔案和進度檔案都是產生在程式的某個檔案夾file中,所以讀取的時候都是讀取的檔案夾下的檔案,從而判斷顯示進度;
後面才知道,由於背景程式有兩台伺服器,導致讀取以及下載的時候找不到相應的檔案夾,兩個伺服器相應的檔案夾弄個共用目錄就可以了
七、相應的後續最佳化
由於下載的檔案多了,導致檔案夾下的檔案越來越多,而原來產生的檔案是沒有價值的,所以加了個定期刪除檔案的功能,只保留近七天的檔案
當然可以用crontab,只不過我比較懶,是在點擊組建檔案的時候,判斷了一下檔案夾中的到期檔案,從而刪除
1 public function execscript_process_show(){ 2 $this->load->library('smarty'); 3 $uid = $_REQUEST['uid']; 4 $url_dir = STATISTIC_EXPORT_FILE_DIR.$uid .'/';//@todo 5 if(!is_dir($url_dir)){ 6 @mkdir($url_dir,0777); 7 } 8 $files = scandir($url_dir); 9 if(!empty($files)){10 foreach ($files as $key => $value) {11 if($value!='.' && $value!='..'){12 foreach ($files as $key => $value) {13 if($value!='.' && $value!='..'){14 if(substr($value, 0 , 5)!="flag_"){15 $filenamedate = substr($value, 0,10);16 $today = date('Y-m-d',time());17 $filenamedate = date('Y-m-d',strtotime($filenamedate)+(STATISTIC_FILE_EXPIRE_DAY-1)*24*3600);18 if($today>$filenamedate){//檔案到期19 @unlink($url_dir . $value);20 @unlink($url_dir . 'flag_' . $value);21 }22 }23 }24 } 25 }26 }27 }28 29 $this->smarty->assign('uid',$uid);30 $this->smarty->display('interact/statistic/execscript.tpl');31 }
八、後記
大檔案的匯出大體就是這個樣子,歡迎大家吐槽,共同交流;
當時在用命令列執行方法的時候,也參考了一下相應的資料,記錄一下;
http://blog.csdn.net/yysdsyl/article/details/4636457http://www.codesky.net/article/201202/163385.htmlhttp://www.cnblogs.com/zdz8207/p/3765567.htmlhttp://blog.163.com/mojian20040228@126/blog/static/4112219320097300922992/http://php.net/manual/en/features.commandline.phphttp://blog.csdn.net/yangjun07167/article/details/5603425http://blog.csdn.net/yunsongice/article/details/5445448http://www.cppblog.com/amazon/archive/2011/12/01/161281.aspxhttp://blog.51yip.com/tag/proc_openhttp://www.justwinit.cn/post/1418/http://limboy.me/tech/2010/12/05/php-async.html
http://www.bkjia.com/PHPjc/1048777.htmlwww.bkjia.comtruehttp://www.bkjia.com/PHPjc/1048777.htmlTechArticle我所經曆的大檔案資料匯出(後台執行,自動產生),資料匯出自動產生 一、前言 記錄一下以前做的後台excel格式匯出統計資訊的功能,也...