所經歷的大檔案資料匯出(後臺執行,自動生成)

thinkyoung發表於2015-08-31

所經歷的大檔案資料匯出(後臺執行,自動生成)

http://www.cnblogs.com/fanfan259/p/4710019.html

 

一、前言

    記錄一下以前做的後臺excel格式匯出統計資訊的功能,也是最近同事問到了相關東西,一時之間竟忘了具體的細節,因此記錄一下;

    大家知道,excel匯出資料的功能,後臺幾乎是必須功能,一般都是點選後,生成檔案然後自動下載,

    如果是資料量小的話,一下子便可請求完成,從而下載到本地;

    但是,如果資料量特別大的時候,頁面就必須一直在等待,直到寫入excel成功,

    這樣便影響了後臺使用者無法操作其他頁面,為此,對excel匯出做了以下功能優化:

  1. excel匯出分成兩部分內容:生成excel檔案和下載excel檔案
  2. excel的檔案生成在程式後臺執行,前端不必等待,可進行其他後臺操作
  3. 增加下載檔案頁面,顯示excel檔案生成的進度,完成後,方可下載生成的excel檔案
  4. 檔案生成後,點選下載方可下載相應的檔案

 二、生成excel檔案

    生成excel檔案的方法有很多,暫不一一記錄,只是記錄本次的方法;

    這裡用到了table的html格式,以及相應的excel的宣告

    (隱約記得其他的方法用office07開啟的時候好像是亂碼,後面嘗試用csv格式檔案,可還是亂碼,所以用了table的形式)

    檔案的開頭:

複製程式碼
 1     $struserdata = <<<Eof
 2         <html xmlns:o="urn:schemas-microsoft-com:office:office"
 3         xmlns:x="urn:schemas-microsoft-com:office:excel"
 4         xmlns="http://www.w3.org/TR/REC-html40">
 5     
 6         <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 7         <html>
 8         <head>
 9             <meta http-equiv="Content-type" content="text/html;charset=utf-8" />
10         <style id="Classeur1_16681_Styles">
11         </style>
12         </head>
13         <body>
14         <div id="Classeur1_16681" align=center x:publishsource="Excel">
15     
16         <table x:str border=1 cellpadding=0 cellspacing=0 width=100% style=`border-collapse: collapse`>
17 Eof;
複製程式碼

    檔案的結尾:

1 $struserdata = <<<Eof
2         </table>
3         </div>
4         </body>
5         </html>
6 Eof;

    當然,檔案中間就是一些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. 第一次請求api的時候,根據返回的total總數,以及pagesize,確定要請求的次數count;
  2. 這樣便可知道要請求api的次數(分頁請求api),在寫入資料檔案的同時,同時寫入進度檔案flag_data.xsl;   
    資料格式大約是(以逗號分割)
        1,5
        2,5
        …
  3. 然後顯示檔案進度的時候,讀取進度檔案,這樣變可知道資料檔案大體的進度
  4. 前端js處理時,幾秒讀取一次相應的方法(如果都100%進度,可停止請求方法),從而實現動態檢視檔案的生成進度

    檢視檔案的進度方法:

 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/4636457

http://www.codesky.net/article/201202/163385.html

http://www.cnblogs.com/zdz8207/p/3765567.html

http://blog.163.com/mojian20040228@126/blog/static/4112219320097300922992/

http://php.net/manual/en/features.commandline.php

http://blog.csdn.net/yangjun07167/article/details/5603425

http://blog.csdn.net/yunsongice/article/details/5445448

http://www.cppblog.com/amazon/archive/2011/12/01/161281.aspx

http://blog.51yip.com/tag/proc_open

http://www.justwinit.cn/post/1418/

http://limboy.me/tech/2010/12/05/php-async.html
複製程式碼

 


相關文章