程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 網頁編程 >> PHP編程 >> 關於PHP編程 >> 我所經歷的大文件數據導出(後台執行,自動生成),數據導出自動生成

我所經歷的大文件數據導出(後台執行,自動生成),數據導出自動生成

編輯:關於PHP編程

我所經歷的大文件數據導出(後台執行,自動生成),數據導出自動生成


一、前言

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

    大家知道,excel導出數據的功能,後台幾乎是必須功能,一般都是點擊後,生成文件然後自動下載,

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

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

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


 二、生成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; View Code

    文件的結尾:

1 $struserdata = <<<Eof 2 </table> 3 </div> 4 </body> 5 </html> 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 .'/';//@todo 10 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_flag 51 ); 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/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

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved