欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  后端开发

小弟我所经历的大文件数据导出(后台执行,自动生成)

程序员文章站 2022-06-05 20:03:59
...
我所经历的大文件数据导出(后台执行,自动生成)

一、前言

记录一下以前做的后台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          3         xmlns:x="urn:schemas-microsoft-com:office:excel" 4         xmlns="http://www.w3.org/TR/REC-html40"> 5      6         span style="color: #0000ff;">PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 7          8                       910         12         13         14         
15 16 17 Eof;View 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. 第一次请求api的时候,根据返回的total总数,以及pagesize,确定要请求的次数count;
  2. 这样便可知道要请求api的次数(分页请求api),在写入数据文件的同时,同时写入进度文件flag_data.xsl;
    数据格式大约是(以逗号分割)
    1,5
    2,5
    ...
  3. 然后显示文件进度的时候,读取进度文件,这样变可知道数据文件大体的进度
  4. 前端js处理时,几秒读取一次相应的方法(如果都100%进度,可停止请求方法),从而实现动态查看文件的生成进度

查看文件的进度方法:

小弟我所经历的大文件数据导出(后台执行,自动生成)
 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:[email protected]/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

4楼Shannon
我目前在用的导出Excel使用了MS的Excel, 需要配置DCOM.,先导出XML格式,再将XML格式转换为Excel格式, 超过50000行自动换页,,在IIS中设置允许最大800M的下载, 导出几年的交易记录毫无压力.,Excel文件超过4M,自动使用7Z压缩.,,使用VBScript, 可以在Windows平台直接运行.
Re: 糖糖果
@Shannon,厉害!如果文件特别大的话,确实应该考虑文件下载的大小限制以及文件的压缩,,也确实是下一步优化的思路,学习了。。。
3楼zjeagle
个人认为大文件还用table的html格式,那叫耍流氓。,文件不是一般的大。
Re: longware
@zjeagle,csv伺候,小一半
Re: 糖糖果
@zjeagle,哈哈...这个确实了,还是那个office07打开的问题记得,所以最终还是用了table,不过用table确实不是很好的办法
2楼zjeagle
csv还是太大了。,建议考虑标准的excel格式,用aspose.cells或者npoi之类吧。
Re: 糖糖果
@zjeagle,确实,有些生成的excel的工具,格式都生成的好,并且也很漂亮,满足的格式也多。。。
1楼zsea
曾经也遇到过大量数据导出Excel,还不许用csv,最后是自己写的导出的功能,一次能导出好几十万条记录到Excel,速度还可以。
Re: 糖糖果
@zsea,哈哈,我也遇到过,有些需求实在是变态,本来能很容易的实现,却不让用。。。
小弟我所经历的大文件数据导出(后台执行,自动生成)

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn核实处理。

相关文章

相关视频


网友评论

文明上网理性发言,请遵守 新闻评论服务协议

我要评论
  • 小弟我所经历的大文件数据导出(后台执行,自动生成)
  • 专题推荐

    作者信息
    小弟我所经历的大文件数据导出(后台执行,自动生成)

    认证0级讲师

    推荐视频教程
  • 小弟我所经历的大文件数据导出(后台执行,自动生成)javascript初级视频教程
  • 小弟我所经历的大文件数据导出(后台执行,自动生成)jquery 基础视频教程
  • 视频教程分类