数据导出

第一种:导出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:

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,如果是那种可以直接导出的数据优先选择这种方法比较好。

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

posted @ 2017-06-21 11:35  金于虎  阅读(187)  评论(0编辑  收藏  举报