2019独角兽企业重金招聘Python工程师标准>>>

最近的项目做到关于表格输出的功能,之前用TP的时候也做过,趁着这次功能比较多样的机会整理一下

本文是基于YII2框架进行开发的,不同框架可能会需要更改

一.普通excel格式表格输出

先是最普通的导出.xls格式的表格。首先先看一下表格在网站的显示效果

这里可以看到整个表格一共是7列。下面来看代码的实现。

1.controller文件

//导出统计public function actionStatistics(){//设置内存ini_set("memory_limit", "2048M");set_time_limit(0);//获取用户ID$id         =   Yii::$app->user->identity->getId();//去用户表获取用户信息$user       =   Employee::find()->where(['id'=>$id])->one();//获取传过来的信息(时间,公司ID之类的,根据需要查询资料生成表格)$params     =   Yii::$app->request->get();$objectPHPExcel = new \PHPExcel();//设置表格头的输出$objectPHPExcel->setActiveSheetIndex()->setCellValue('A1', '代理公司');$objectPHPExcel->setActiveSheetIndex()->setCellValue('B1', '收入');$objectPHPExcel->setActiveSheetIndex()->setCellValue('C1', '成本');$objectPHPExcel->setActiveSheetIndex()->setCellValue('D1', '稿件数');$objectPHPExcel->setActiveSheetIndex()->setCellValue('E1', '毛利(收入-成本)');$objectPHPExcel->setActiveSheetIndex()->setCellValue('F1', '毛利率(毛利/收入)*100%');$objectPHPExcel->setActiveSheetIndex()->setCellValue('G1', 'ARPU值');//跳转到recharge这个model文件的statistics方法去处理数据$data = Recharge::statistics($params);//指定开始输出数据的行数$n = 2;foreach ($data as $v){$objectPHPExcel->getActiveSheet()->setCellValue('A'.($n) ,$v['company_name']);$objectPHPExcel->getActiveSheet()->setCellValue('B'.($n) ,$v['company_cost']);$objectPHPExcel->getActiveSheet()->setCellValue('C'.($n) ,$v['cost']);$objectPHPExcel->getActiveSheet()->setCellValue('D'.($n) ,$v['num']);$objectPHPExcel->getActiveSheet()->setCellValue('E'.($n) ,$v['gross_margin']);$objectPHPExcel->getActiveSheet()->setCellValue('F'.($n) ,$v['gross_profit_rate']);$objectPHPExcel->getActiveSheet()->setCellValue('G'.($n) ,$v['arpu']);$n = $n +1;}ob_end_clean();ob_start();header('Content-Type : application/vnd.ms-excel');//设置输出文件名及格式header('Content-Disposition:attachment;filename="代理公司统计'.date("YmdHis").'.xls"');//导出.xls格式的话使用Excel5,若是想导出.xlsx需要使用Excel2007$objWriter= \PHPExcel_IOFactory::createWriter($objectPHPExcel,'Excel5');$objWriter->save('php://output');ob_end_flush();//清空数据缓存unset($data);}

2.model文件

<?php
namespace app\models;//model层的命名空间//注意要引用yii的arrayhelper
use yii\helpers\ArrayHelper;
use Yii;class Recharge extends \yii\db\ActiveRecord
{//excel一次导出条数const EXCEL_SIZE = 10000;//统计导出public static function statistics($params){//导出时间条件if(empty($params['min'])){$date_max = date("Y-m-d",strtotime("-1 day"));$date_min = date("Y-m-d",strtotime("-31 day"));}else{$date_min = $params['min'];$date_max = $params['max'];}$where = '';$where .= '(`issue_date` BETWEEN '.'\''.$date_min.'\''.' AND '.'\''.$date_max.'\')';//查找指定数据$sql = 'selectarticle.company_id,article.cost,article.company_costfrom article WHERE article.status=2 AND '.$where;$article = Article::findBySql($sql)->asArray()->all();$article = ArrayHelper::index($article,null,'company_id');$companys = [];foreach ($article as $key=>$v){if(empty($key)){continue;}else{$number         =   count($v);$company        =   Company::find()->where(['id'=>$key])->select('name')->one();$company_name   =   $company['name'];$cost           =   0;$company_cost   =   0;foreach ($v as $n){$cost += $n['cost'];$company_cost += $n['company_cost'];}if($company_cost == 0){$company_cost =1;}//这里注意,数据的存储顺序要和输出的表格里的顺序一样$companys[] = [//公司名'company_name'      =>  $company_name,//收入'company_cost'      =>  $company_cost,//成本'cost'              =>  $cost,//稿件数'num'               =>  $number,//毛利'gross_margin'      =>  $company_cost-$cost,//毛利率'gross_profit_rate' =>  round(($company_cost-$cost)/$company_cost*100,2).'%',//ARPU值'arpu'              =>  round($company_cost/$number,2),];}}return $companys;}
}

最终导出的效果(单元格大小导出后调整过)可以看到和网页显示的基本一样。

二.大数据表格导出

这时老板说了,我们不能只看总和的数据,最好是把详细数据也给导出来。既然老板发话了,那就做吧。还是按照第一种的方法去做,结果提示我php崩溃了,再试一次发现提示写入字节超出。打开php的配置文件php.ini

memory_limit = 128M

发现默认内存已经给到128M,应该是足够的了。于是我打开数据库一看,嚯!

接近83万条的数据进行查询并导出,可不是会出问题嘛!怎么办呢,于是我Google了一下,发现对于大数据(2万条以上)的导出,最好是以.csv的形式。不说废话,直接上代码

1.controller文件

 //导出清单public function actionInventory(){ini_set("memory_limit", "2048M");set_time_limit(0);$id         =   Yii::$app->user->identity->getId();$user       =   Employee::find()->where(['id'=>$id])->one();$params     =   Yii::$app->request->get();//类似的,跳转到recharge这个model文件里的inventory方法去处理数据$data       =   Recharge::inventory($params);//设置导出的文件名$fileName   =   iconv('utf-8', 'gbk', '代理商统计清单'.date("Y-m-d"));//设置表头$headlist   =   array('代理商','文章ID','文章标题','媒体','统计时间范围','状态','创建时间','审核时间','发稿时间','退稿时间','财务状态','成本','销售额','是否是预收款媒体类型','订单类别');header('Content-Type: application/vnd.ms-excel');//指明导出的格式header('Content-Disposition: attachment;filename="'.$fileName.'.csv"');header('Cache-Control: max-age=0');//打开PHP文件句柄,php://output 表示直接输出到浏览器$fp = fopen('php://output', 'a');//输出Excel列名信息foreach ($headlist as $key => $value) {//CSV的Excel支持GBK编码,一定要转换,否则乱码$headlist[$key] = iconv('utf-8', 'gbk', $value);}//将数据通过fputcsv写到文件句柄fputcsv($fp, $headlist);//每隔$limit行,刷新一下输出buffer,不要太大,也不要太小$limit = 100000;//逐行取出数据,不浪费内存foreach ($data as $k => $v) {//刷新一下输出buffer,防止由于数据过多造成问题if ($k % $limit == 0 && $k!=0) {ob_flush();flush();}$row = $data[$k];foreach ($row as $key => $value) {$row[$key] = iconv('utf-8', 'gbk', $value);}fputcsv($fp, $row);}}

2.model文件(因为这部分我要处理的过多,所以只选择了部分代码),在查询数据那部分,因为要查的数据较多,所以可以结合我之前写的关于Mysql大数据查询处理的文章看一下

PHP

//清单导出public static function inventory($params){//统计时间范围if(!empty($params['min']) && !empty($params['max'])){$ti = strtotime($params['max'])+3600*24;$max = date('Y-m-d',$ti);$time = $params['min'].'-'.$params['max'];$date_min = $params['min'];$date_max = $max;}else{$date_max = date('Y-m-d');$date_min = date('Y-m-d',strtotime("-31 day"));$time = $date_min.'-'.$date_max;}//查询数据if($params['state'] == 1){$where  = '';$where .= ' AND (`issue_date` BETWEEN '.'\''.$date_min.'\''.' AND '.'\''.$date_max.'\')';$map = 'selectcompany.name,article.id,article.title,media.media_name,article.status,article.created,article.audit_at,article.issue_date,article.back_date,article.finance_status,article.cost,article.company_cost,media.is_advancefrom articleLEFT JOIN custom_package ON custom_package.id = article.custom_package_idLEFT JOIN `order`        ON custom_package.order_id = `order`.`id`LEFT JOIN company        ON company.id = article.company_idLEFT JOIN media          ON media.id = article.media_idwhere article.status=2   and `order`.package=0'.$where;//查找的第一部分数据,使用asArray方法可以使我们查找的结果直接形成数组的形式,没有其他多余的数据占空间(注意:我这里查找分三部分是因为我要查三种不同的数据)$list1   = Article::findBySql($map)->asArray()->all();$where2  = '';$where2 .= ' AND (`issue_date` BETWEEN '.'\''.$date_min.'\''.' AND '.'\''.$date_max.'\')';$where2 .= ' AND (`back_date` > \''.$date_max.'\')';$map2 = 'selectcompany.name,article.id,article.title,media.media_name,article.status,article.created,article.audit_at,article.issue_date,article.back_date,article.finance_status,article.cost,article.company_cost,media.is_advancefrom articleLEFT JOIN custom_package ON custom_package.id = article.custom_package_idLEFT JOIN `order`        ON custom_package.order_id = `order`.`id`LEFT JOIN company        ON company.id = article.company_idLEFT JOIN media          ON media.id = article.media_idwhere article.status=3   and `order`.package=0 '.$where2;//查找的第二部分数据$list2 = Article::findBySql($map2)->asArray()->all();$where3 = '';$where3 .= ' AND (`issue_date` BETWEEN '.'\''.$date_min.'\''.' AND '.'\''.$date_max.'\')';$map3 = 'selectcompany.name,article.id,article.title,media.media_name,article.status,article.created,article.audit_at,article.issue_date,article.back_date,article.finance_status,article.cost,article.company_cost,media.is_advancefrom articleLEFT JOIN custom_package ON custom_package.id = article.custom_package_idLEFT JOIN `order`        ON custom_package.order_id = `order`.`id`LEFT JOIN company        ON company.id = article.company_idLEFT JOIN media          ON media.id = article.media_idwhere article.status=5 '.$where3;//查找的第三部分数据$list3 = Article::findBySql($map3)->asArray()->all();$list4 = ArrayHelper::merge($list1,$list2);$list = ArrayHelper::merge($list4,$list3);}//把结果按照显示顺序存到返回的数组中if(!empty($list)){foreach ($list as $key => $value){//代理公司$inventory[$key]['company_name']    =   $value['name'];//文章ID$inventory[$key]['id']              =   $value['id'];//文章标题$inventory[$key]['title']           =   $value['title'];//媒体$inventory[$key]['media']           =   $value['media_name'];//统计时间$inventory[$key]['time']            =   $time;//状态switch($value['status']){case 2:$inventory[$key]['status']  = '已发布';break;case 3:$inventory[$key]['status']  = '已退稿';break;case 5:$inventory[$key]['status']  = '异常稿件';break;}//创建时间$inventory[$key]['created']         =   $value['created'];//审核时间$inventory[$key]['audit']           = $value['audit_at'];//发稿时间$inventory[$key]['issue_date']      = $value['issue_date'];//退稿时间$inventory[$key]['back_date']       = $value['back_date'];//财务状态switch($value['finance_status']){case 0:$inventory[$key]['finance_status'] = '未到结算期';break;case 1:$inventory[$key]['finance_status'] = '可结算';break;case 2:$inventory[$key]['finance_status'] = '资源审批中';break;case 3:$inventory[$key]['finance_status'] = '财务审批中';break;case 4:$inventory[$key]['finance_status'] = '已结款';break;case 5:$inventory[$key]['finance_status'] = '未通过';break;case 6:$inventory[$key]['finance_status'] = '财务已审批';break;}//成本$inventory[$key]['cost']            = $value['cost'];//销售额$inventory[$key]['company_cost']    = $value['company_cost'];//是否是预售switch($value['is_advance']){case 0:$inventory[$key]['is_advance']   = '否';break;case 1:$inventory[$key]['is_advance']   = '是';break;case 2:$inventory[$key]['is_advance']   = '合同';break;}//订单类别switch($params['state']){case 1:$inventory[$key]['order_type'] = '时间区间无退稿完成订单';break;case 2:$inventory[$key]['order_type'] = '时间区间发布前退稿订单';break;case 3:$inventory[$key]['order_type'] = '时间区间发布后时间区间退稿订单';break;case 4:$inventory[$key]['order_type'] = '时间区间之前发布时间区间内退稿订单';break;case 5:$inventory[$key]['order_type'] = '异常订单';break;}}}else{$inventory[0]['company_name']    =   '无数据导出';}return $inventory;}

3.导出结果

导出数量

 

导出的文件

基本上可以保证整个过程在2~4秒内处理完成

三.合并单元格

老板一看做的不错,说你顺便把充值统计的导出也做了把,想想我都是处理过这么多数据的人了,还不是分分钟搞定的事?来,上原型图

噗,一口老血,话都说了,搞吧。在做的时候我发现,这次的导出主要是要解决单元格合并的问题。经过查资料发现,PHP本身是实现不了单元格合并的,于是我打算通过phpexcel来实现

如果是使用PHPExcel的话,基本操作是这样的(合并A1到E1)

$objPHPExcel->getActiveSheet()->mergeCells('A1:E1');
// 表格填充内容
$objPHPExcel->getActiveSheet()->setCellValue('A1','The quick brown fox.');

结果

或者这样的(合并A1到E4)

$objPHPExcel->getActiveSheet()->mergeCells('A1:E4');
$objPHPExcel->getActiveSheet()->setCellValue('A1','The quick brown fox.');

结果

这样并不能满足我的要求,首先它是一个一个合并的,其次我要显示的充值金额下面的类型是会变化的,不可能固定写死,然后每次都更改。所以放弃了这种方法。

后来在小伙伴的帮助下尝试用html转存excel的方法

1.方法文件(因为我要每天定时执行,所以并没有写到controller层)

public function actionExcelRechargeStatistics(){//先定义一个excel文件$filename   =   date('【充值统计表】('.date('Y-m-d').'导出)').".xls";header("Content-Type: application/vnd.ms-execl");header("Content-Type: application/vnd.ms-excel; charset=utf-8");header("Content-Disposition: attachment; filename=$filename");header("Pragma: no-cache");header("Expires: 0");//时间条件if(empty($params['min'])){$time       =   date('Y-m-d',strtotime("+1 day"));$where      =   ' created < \' '.$time.'\'';}else{$time       =   $params['min']+3600*24;$time_end   =   $params['max']+3600*24;$where      =   ' created <= \' '.$time_end.'\' AND created >= \''.$time.'\' ';}//充值类型列表$recharge_type  =   Recharge::find()->asArray()->all();if(empty($recharge_type)){$rechargelist[0]=   '';}else{$rechargelist   =   ArrayHelper::map($recharge_type,'id','recharge_name');}$rechargelist1      =   $rechargelist;$count              =   count($rechargelist1);//使用html语句生成显示的格式$excel_content      =   '<meta http-equiv="content-type" content="application/ms-excel; charset=utf-8"/>';$excel_content     .=   '<table border="1" style="font-size:14px;">';$excel_content     .=   '<thead><tr><th rowspan="2">ID</th><th rowspan="2">公司名称</th><th colspan='.$count.'>充值金额</th><th rowspan="2">充值大小</th><th rowspan="2">实际消费</th><th rowspan="2">当前余额</th></tr><tr>';foreach ($rechargelist1 as $v => $t){$excel_content     .=   '<th colspan="1">'.$t.'</th>';}$excel_content     .=   '</tr></thead>';//查找最新的固化数据$search = RechargeStatistics::find()->where($where)->asArray()->all();if(!empty($search)){foreach ($search as $key => $value){$search[$key]['recharge'] = unserialize($value['recharge']);}}//html语句填充数据if(empty($search)){}else{foreach ($search as $k) {$excel_content  .= '<td>'.$k['company_id'].'</td>';$excel_content  .= '<td>'.$k['company_name'].'</td>';foreach ($rechargelist1 as $v=>$t){$price = 0;foreach ($k['recharge'] as $q=>$w){if($w['recharge_id'] == $v){$price = $w['price'];break;}}$excel_content  .= '<td>'.$price.'</td>';}$excel_content  .= '<td>'.$k['total'].'</td>';$excel_content  .= '<td>'.$k['consume'].'</td>';$excel_content  .= '<td>'.($k['total']-$k['consume']).'</td></tr>';}}$excel_content  .=  '</table>';echo $excel_content;die;
}

2.结果

到这里基本就完成所有的任务了!

转载于:https://my.oschina.net/u/1587469/blog/1456656

YII2框架的excel表格导出相关推荐

  1. 清华大学赵志磊--基于thinkPHP6框架的Excel表格导入和导出

    1.Excel表格导出 先上大图:(可放大查看,当作壁纸再好不过,超高清壁纸) 正题: 首先.thinkPHP6框架内在admin应用下,自定义文件夹,存放公共类,里面封装公共方法. 命名空间写好,记 ...

  2. 通用Excel表格导出(Map类型数据导出为表格)

    背景 为提升代码开发效率,项目使用了通用查询(动态数据表.动态条件.动态列名等),各表查询通过同一个页面展现,前端通过获取路径上的表名调用同一个后端控制器--动态获取到查询条件.数据列名.不同表数据等 ...

  3. 微信小程序云开发之用户输入数据后excel表格导出升级版

    大家好,我是csdn的小博主lqj_本人,最近在哔哩哔哩开始上传我的制作微信小程序的详细流程,大家可以关注一下哔哩哔哩:小淼前端 本次程序的详细视频教程已上传至哔哩哔哩: 腾讯云开发小程序之用户输入数 ...

  4. vue数组转Excel表格导出

    vue数组转Excel表格导出 安装依赖 npm i xlsx vue组件 <template><div><el-button type="success&qu ...

  5. linux脚本的数据输出到excel,使用shell实现Excel表格导出功能 | 剑花烟雨江南

    在Web项目中,我们经常会遇到Excel表格导出的功能,对于一些数据实时性要求不高的.逻辑相对简单的导出,是否可用通过shell脚本的方式来进行导出,从而降低开发成本呢? 我们都知道,CSV格式可以用 ...

  6. js导出变量 vue_vue.js前端实现excel表格导出和获取headers里的信息

    前段时间写过一篇文章基于element实现后台管理系统,并提到excel表格导出功能,可能描述不是很详细,现在单独整理列出. 后端提供的接口: // 下载分拣列表 export function ge ...

  7. C# Winfrom Excel表格导出 Aspose.Cells超简单方式

    C# Winfrom Excel表格导出 Aspose.Cells超简单方式 首先需要下载 Aspose.Cells.dll,Aspose.Slides.dll,Aspose.Words.dll 这三 ...

  8. thinkphp excel表格导出

    Thinkphp里实现excel表格导出数据,需要在网上下载PHPExcel类包,放在Vendor文件夹下面 地址:http://phpexcel.codeplex.com/releases/view ...

  9. excel表格导出之后身份证号列变成了科学计数法

    excel表格导出之后身份证号列变成了科学计数法 解决:写sql查询出所有数据,并在身份证列添加字符,然后导出,将要复制的excel表格设置单元格格式问文本类型,然后复制粘贴,再把加入的字符删除,搞定 ...

最新文章

  1. java中的多态_Java中的多态
  2. 功能强大的打包工具 NSIS,全部用脚本搞定 使用经验总结帖(持续更新中。。。)...
  3. 出色技术团队的关键要素
  4. phpmyadmin 批量导出多个数据库
  5. python数据类型介绍_python的数据类型简介
  6. java字符串装双精度_Java 将双精度值转换为字符串
  7. 软件工程 – 开发模型
  8. jquery mysql实现加入购物车_jQuery实现加入购物车飞入动画效果
  9. jenkins组权限_Jenkins 中基于角色的权限管理
  10. java mongodb 时间查询_Spring-Data-MongoDB快速入门
  11. qfile.remove 删除已经被加载的文件_Milvus数据管理:删除的实现原理
  12. GPS 射频调试总结
  13. Spring Bean生命周期总结
  14. 【软考】系统集成项目管理工程师(五)项目立项管理
  15. 【文献心得】关于内核在不同芯片平台移植的相关整理
  16. 《梁宁产品思维30讲》
  17. luogu P1357 花园
  18. 天行健,君子以自强不息;地势坤,群子以厚德载物。
  19. 系统设计-开放平台(一)
  20. Android APP切换系统语言

热门文章

  1. java path设置错误_linux下环境变量PATH设置错误的补救
  2. oracle 11查询sid,oracle 11g 更改sid和dbname
  3. javascript BOM对象详解
  4. python二维表转一维表_Excel、Power BI及Python系列:使用Power BI转化一维表与二维表...
  5. java 循环关键字_Java循环结构_常量_关键字
  6. 安装lynis_lynis安装和扫描Linux的安全漏洞
  7. el table怎么获取最后一行_elementUi表格插入额外一行怎么操作
  8. 文明使用计算机 教案,四年级信息技术下册 文明在我身边教案 华中师大版
  9. 全国计算机在线做题,2020全国一级计算机基础及MS Office应用考试在线自测试题库(不限设备,登陆即可做题)...
  10. Redis的数据类型详解