总结ThinkPHP导出数据到表格的几种方法及代码,分享给大家,注释超详细。

来源:互联网 发布:知乎非常了得 编辑:程序博客网 时间:2024/05/18 13:28

我先具体介绍完这几种方法,然后在结尾会将几种办法进行优缺点的分析和比较,方便大家择优利用。

第一种:导出excel格式,这种方法适合数据量不太大的情况,比如下面这段代码是勾选导出。

public function excelExport(){
        $ids = substr($_REQUEST['ids'],0);//传到这个方法的数据id
        import("ORG.PHPExcel.PHPExcel");//引入PHPExcel引擎
        $objPHPExcel = new PHPExcel();    
        $objProps = $objPHPExcel->getProperties();
        $objProps->setCreator("smartcrm");//灰色的代码就是给导出的表格设置一些属性,无需变动
        $objProps->setLastModifiedBy("smartcrm"); 
        $objProps->setTitle("smartcrm");    
        $objProps->setSubject("smartcrm");    
        $objProps->setDescription("smartcrm");    
        $objProps->setKeywords("smartcrm");    
        $objProps->setCategory("smartcrm");
        $objPHPExcel->setActiveSheetIndex(0);     
        $objActSheet = $objPHPExcel->getActiveSheet(); 
        $objActSheet->setTitle('Sheet1');
        $row = 1;   //行坐标
        $col = "A"; //列坐标
        $field_list = M()->select();//查出要导出的数据的字段列表
        foreach($field_list as $field){
            $objActSheet->setCellValue($col.$row ,$field['name']);//将字段的名字循环放在第一行
            $col ++;
        }
        $list = M()->where('id in(%s)',$ids)->select();//查询要导出的数据
        foreach ($list as $k => $v) {//循环写入excel表格,先循环行,再循环列,一个单元格一个单元的写
            $row ++;//第一行写完,行累加
            $col = "A"; //列坐标 
            foreach($field_list as $field){//这里可以对导出数据按照字段进行处理,比如时间戳转化为日期格式等等等。
                 $objActSheet->setCellValue($col.$row, $v[$field['field']]);
                 $col++;//第一行第一列写完,列累加
            }
        }
        $objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel5');
        header("Content-Type: application/vnd.ms-excel;");
        header("Content-Disposition:attachment;filename=XXX.xls");//XXX定义导出Excel的名字
        header("Pragma:no-cache");
        header("Expires:0");

        $objWriter->save('php://output');
}

第二种:导出csv格式,这种方法适合数据量大的情况,比如一次性全部导出(兼容个别导出)等等,折后总导出方式的原理是分批次循环导出,防止查询压力过大导致的崩溃。

public function excelExportAll(){
        $ids = !!$_REQUEST['ids'] ? $_REQUEST['ids'] : false;
        $fileDir=(substr(dirname(__FILE__),0,strpos(dirname(__FILE__),'\App'))."/App/Runtime/Temp/");
        if(!is_dir($fileDir)) mkdir($fileDir);//设置文件生成的路径
        $fileName=substr(basename(__FILE__),0,strpos(basename(__FILE__),'.'));
        $fileTime=time();
        $fileType=".csv";
        $file=$fileDir.$fileName.'-'.$fileTime.$fileType;设置导出文件的名称,这么长是为了防止重复,保证生成文件的唯一性
        $fp=fopen($file,"w+");//以写入的方式打开文件
        $field_list = M()->select();//获取所有导出字段
        $title_name    = array();
        foreach($field_list as $column=>$detail) {//循环写入标题
            array_push($title_name,utf2gb($detail['name']));//这里注意设计汉字的需要转码,否则导出来你会看到火星文
        }
        fputcsv($fp,$title_name);
        $where =  'id in('.$ids.")";
        $count  = M()->where($where)->count();
        $once_c =1000; //手动分批次,这里根据服务器性能和内存情况适量定义
        $cur_s  = 0;
        $run_time=floor($count/$once_c);
        for($i=0;$i<=$run_time;$i++){
          $list = M()->limit($cur_s.",".$once_c)->select();//每次只查询$once_c条数据
          foreach ($list as $k => $row) {
               $array_column[$k] = $row;//把 数据都放在$array_column这个数组里
               foreach($row as $c=>$v) {   
                     //这里可进行一系列转化操作,如时间戳的字段转化为日期格式等,转化的字段和内容如果很多的话会使内存随着循环叠加上升,我利用任务管理器做过测试,下面我会详细介绍。
               } 
               fputcsv($fp,$array_column[$k]);//把$array_column数组循环写入到打开文件中
           }
           $cur_s += $once_c;//这个批次循环完,累加循环下一批
        }
        if(fclose($fp)){//写完关闭文件
            $down_name=$fileName;
            $content=file_get_contents($file);
            unlink($file);
            header("Content-Type: text/csv;charset=utf-8"); 
            header("Content-Type: application/force-download");
            header("Content-Type: application/octet-stream");
            header("Content-Type: application/download");
            header("Content-Disposition: attachment; filename=$down_name".".csv"); 
            header('Cache-Control:must-revalidate,post-check=0,pre-check=0'); 
            header('Expires:0'); 
            header('Pragma:public');
            echo $content;
        }
 }

第三种:这种办法是我在大神指点下做出来的,画面比较炫酷,单独做了导出的页面,下面我会 附张图。数据量大的时候会有动态进度条以及预估的下载时间,当然我用的 进度条插件是ui-progressbars.js和animation.js,弹层插件是layer,下面我把后台代码以及html页面分享给大家。

HTML:

<script src="__PUBLIC__/js/ui-progressbars.js"></script>
<script src="__PUBLIC__/js/animation.js"></script>
<div class="tab-content" style="border-bottom:0px;">
<div class="form-group">
<div class="progress progress-striped active">
   <div id="progress-bar" role="progressbar" style="width:0" class="progress-bar"></div>
</div>
</div>
<p id="tips"></p>
<div class="form-group">
<label class="col-sm-5 control-label"></label>
<div id="btn_download" class="col-sm-7"></div>
</div>
</div>
<script>
$(function(){
Excelexport();
function Excelexport(option){//把返回的json格式数据整理后继续请求
$.ajax({
type: 'GET',
data: option,
url: 'index.php?m=cdr&a=excelExportAll&ids={$ids}',
dataType: "json",
success: function(data){
if(!data.is_end){
var progress = Math.round(data.cur_s/data.count*100) +"%";
$("#tips").html("共 "+data.count+" 条数据,大约需要 "+data.total+" 秒,请耐心等待。");
$("#progress-bar").css('width',progress);
$("#progress-bar").html(progress);
var e_opt = {file:data.file,step:data.step,cur_s:data.cur_s,count:data.count,fileName:data.fileName};
Excelexport(e_opt);
}else{//导出完毕后出现下载按钮,点击调用后台下载方法实现下载
layer.title('导出完毕。感谢您的耐心等待!');
progress = '100%';
$("#tips").html("导出完成,请点击下载。");
$("#progress-bar").css('width',progress);
$("#progress-bar").html(progress);
$("#btn_download").html("<a onclick='javascript:layer.closeAll();' href='index.php?m=cdr&a=excelDownload&fileName="+data.fileName+"&file="+data.file+"' class='btn btn-primary'>下载</a>");
}
}
});
}
});
</script>


PHP:

//导出的代码,就是前台ajax不停请求的方法

public function excelExportAll(){
$ids = !!$_REQUEST['ids'] ? $_REQUEST['ids'] : false;
        $fileDir=(substr(dirname(__FILE__),0,strpos(dirname(__FILE__),'\App'))."/App/Runtime/Temp/");
        if(!is_dir($fileDir)) mkdir($fileDir);//设置文件生成的路径
        $fileName=substr(basename(__FILE__),0,strpos(basename(__FILE__),'.'));
        $fileTime=time();
        $fileType=".csv";
        $file=$fileDir.$fileName.'-'.$fileTime.$fileType;设置导出文件的名称,这么长是为了防止重复,保证生成文件的唯一性
        $fp=fopen($file,"w+");//以写入的方式打开文件
        $field_list = M()->select();//获取所有导出字段
        $title_name    = array();
        foreach($field_list as $column=>$detail) {//循环写入标题
            array_push($title_name,utf2gb($detail['name']));//这里注意设计汉字的需要转码,否则导出来你会看到火星文
        }

        $step = !!$_REQUEST['step'] ? $_REQUEST['step'] : 0;//方法一方法二唯一的区别在于此,把方法二循环的原理改为前台ajax请求,这样巧妙地的防止了内存的增长
        if($step == 0) {   
            fputcsv($fp,$title_name);
        }
        $where =  'id in('.$ids.")";
        $once_c =10000; //手动分批次,这里根据服务器性能和内存情况适量定义
        $cur_s  = !!$_REQUEST['cur_s'] ? $_REQUEST['cur_s'] : 0;

$limit  = $cur_s.",".$once_c;

        $once_c = 10000;
        $count  = $step == 0 ? $m->where($where)->count() : $_REQUEST['count'];

$run_time=floor($count/$once_c);
        $list = $m->where($where)->limit($limit)->select();

 foreach ($list as $k => $row) {
               $array_column[$k] = $row;//把 数据都放在$array_column这个数组里
    
      //这里可进行一系列转化操作,如时间戳的字段转化为日期格式等。
               fputcsv($fp,$array_column[$k]);//把$array_column数组循环写入到打开文件中 fputcsv($fp,$array_column[$k]);//把$array_column数组循环写入到打开文件中
         }
   
        $cur_s += $once_c;
        $step  ++;
        if($step > $run_time) {
            $is_end = true;  
        }else{
            $is_end = false; //最后一次循环的时候告诉前台停止ajax请求
        }
        $time = sec_format($run_time * 1.9);
        fclose($fp);
        $download['file'] = urlencode($file);
        $download['is_end'] = $is_end;
        $download['total'] = $time;
        $download['step'] = $step;
        $download['cur_s'] = $cur_s;
        $download['count'] = $count;
        $download['fileName'] = $fileName;
        echo json_encode($download);//把一系列参数整理后以json格式返回
        exit;
}
//下载的代码
public function excelDownload(){
        set_time_limit(0);  
        ini_set('memory_limit', '1024M');//这两句是为了防止数据库崩溃,临时扩大内存,按需修改。
        ob_start();
        $down_name=$_REQUEST['fileName'];
        header("Content-Type: text/csv;charset=utf-8"); 
        header("Content-Type: application/force-download");
        header("Content-Type: application/octet-stream");
        header("Content-Type: application/download");
        header("Content-Disposition: attachment; filename=$down_name".".csv"); 
        header('Cache-Control:must-revalidate,post-check=0,pre-check=0'); 
        header('Expires:0'); 
        header('Pragma:public');
        readfile($_REQUEST['file']);
        ob_flush();

}

第四种:方法三稍作修改就可以实现像Mysql数据库导出那样的效果,即点击导出后,浏览器下方出现下载文件,随着导出的进行,前面一个小圈就不停的转啊转,直到下载完毕。他们其实就是把导出的东西放在浏览器进行,边导出边写入,只需要把上面 fputcsv($fp,$array_column[$k])这句代码改成echo impolde(',',$array_column[$k]).'/r/n'就可以了,其余的前台页面等等的都不要了,差不多是下面这个意思。

public function excelExportAll(){
$ids = !!$_REQUEST['ids'] ? $_REQUEST['ids'] : false;
        $fileDir=(substr(dirname(__FILE__),0,strpos(dirname(__FILE__),'\App'))."/App/Runtime/Temp/");
        if(!is_dir($fileDir)) mkdir($fileDir);//设置文件生成的路径
        $fileName=substr(basename(__FILE__),0,strpos(basename(__FILE__),'.'));
        $fileTime=time();
        $fileType=".csv";
        $file=$fileDir.$fileName.'-'.$fileTime.$fileType;设置导出文件的名称,这么长是为了防止重复,保证生成文件的唯一性
        $fp=fopen($file,"w+");//以写入的方式打开文件
        $field_list = M()->select();//获取所有导出字段
        $title_name    = array();
        foreach($field_list as $column=>$detail) {//循环写入标题
            array_push($title_name,utf2gb($detail['name']));//这里注意设计汉字的需要转码,否则导出来你会看到火星文
        }
        $step = !!$_REQUEST['step'] ? $_REQUEST['step'] : 0;//方法一方法二唯一的区别在于此,把方法二循环的原理改为前台ajax请求,这样巧妙地的防止了内存的增长
        if($step == 0) {   
            fputcsv($fp,$title_name);
        }
        $where =  'id in('.$ids.")";
        $once_c =10000; //手动分批次,这里根据服务器性能和内存情况适量定义
        $cur_s  = !!$_REQUEST['cur_s'] ? $_REQUEST['cur_s'] : 0;

$limit  = $cur_s.",".$once_c;

        $once_c = 10000;
        $count  = $step == 0 ? $m->where($where)->count() : $_REQUEST['count'];

$run_time=floor($count/$once_c);
        $list = $m->where($where)->limit($limit)->select();

 foreach ($list as $k => $row) {
               $array_column[$k] = $row;//把 数据都放在$array_column这个数组里
    
      这里可进行一系列转化操作,如时间戳的字段转化为日期格式等。
           
   echo impolde(',',$array_column[$k]).'/r/n';
         }
    
        $cur_s += $once_c;
        $step  ++;
        if($step > $run_time) {
ob_start();
        $down_name=$_REQUEST['fileName'];
        header("Content-Type: text/csv;charset=utf-8"); 
        header("Content-Type: application/force-download");
        header("Content-Type: application/octet-stream");
        header("Content-Type: application/download");
        header("Content-Disposition: attachment; filename=$down_name".".csv"); 
        header('Cache-Control:must-revalidate,post-check=0,pre-check=0'); 
        header('Expires:0'); 
        header('Pragma:public');
        readfile($_REQUEST['file']);
        ob_flush();
        }else{

exit;

}
}


重点来了,分析一下上述4种方法的利弊:

第一种:适用于数据量小的导出,优点是导出的xls格式,不会导致乱码的问题,但数据量大的话会直接崩掉。

第二种:优点是可以导出大量的数据,但比较依靠服务器内存,因为如果你导出的数据需要处理的情况比较多,尤其是需要关联其他表转化等等的时候,内存依然会随着循环而增长,这样如果数据量很大的话依然会导致崩的问题,而且相比第四种情况,这种方法导出大量数据的时候会显得比较死板,没有像方法三炫酷的进度提示,也没有方法四那样浏览器下方的运行状态。

第三种:这种方法相对而言对于各种情况最兼容的办法,当然如果你不需要转化很多数据,可以用方法二或者四,因为方法三也有缺点,比如下载时依然会依赖内存,导出完毕时必须下载略显繁琐等等,希望广大技术大牛继续探讨完善。

第四种:简单直观的导出方式,我认为i,如果是那种可以直接导出的数据优先选择这种方法比较好。

凡事皆有利弊,择优录取。这是我第一次写博客,以后依然会把我所掌握的,总结的方法陆续更新上来,或者有什么好的技术也会分享给大家,我也是刚入行不到一年的新手,望有什么错误请谅解,共同探讨学习。

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 注册消防工程师好考吗 一级消防好考吗 二级注册消防工程师报名时间 注册消防工程师前景 消防工程师考试题型 助理消防工程师 一级消防工程师论坛 注册消防工程师考试难度 一级消防工程师考试题型 注册二级消防工程师 注册消防工程师论坛 一级注册消防工程师教材 注册消防工程师报名 二级消防工程师考试科目 消防工程师报考条件二级 二级注册消防工程师报考条件 一级注册消防工程师论坛 二级消防工程师好考吗 一级注册消防工程师招聘 注册消防工程师难考吗 考一级消防工程师有用吗 消防工程师难度 一消防工程师 一级消防工程师考试难度 一级消防工程师好考吗 消防工程师待遇 注册消防工程师一级二级区别 一级注册消防工程师好考吗 注册消防工程师好不好考 一级消防工程师难度 二级注册消防工程师考试科目 二级消防考试时间 一级消防工程师招聘 陕西二级消防工程师报名时间 2017消防工程师考试时间 消防工程师代报名 消防技术综合能力 一级注册消防工程师报考条件 河北二级消防工程师报名时间 二级消防工程师报名条件 消防员考试报考条件