liamqm

PHPExcel使用体会

因为毕设导师智能分配系统的需要,系负责人在管理学生和导师时,希望可以使用Excel批量导入学生和导师的信息,学长的报课系统使用的是PHPExcel的类库,于是我也抽空花了2天的时间学习了PHPExcel的最基础的导入导出功能的实现,总结了自己的使用心得和一些常用的使用方法

一、类库的引入

  • PHP我使用的是ThinkPHP5的框架,所以以TP5框架为例,这是项目的PHPExcel的目录结构:

      require_once \'extend/reader.php\'; //Excel读取
      require_once \'extend/PHPExcel_1.8.0_doc/Classes/PHPExcel.php\'; //Excel导入导出
    

二、Excel导入

  • 调用Reader

      require_once \'extend/reader.php\';
    
  • 创建Reader

      $data = new \Spreadsheet_Excel_Reader();
    
  • 设置在页面中输出的编码方式

      $data->setOutputEncoding(\'utf-8\');
    
  • 读取上传到当前目录下实际路径为$realPath的文件

      $data->read($realPath);
    
  • 设置PHP的报错级别并返回当前的级别

      error_reporting(E_ALL ^ E_NOTICE);
      或error_reporting(E_ALL & ~E_NOTICE);
    

    起初并不是很懂这句代码的含义,经过查找资料后发现,在不同的PHP版本中,原本可能在低版本的PHP中运行正常的代码,在较高版本的PHP中运行可能就会出现报错,为了使程序能够正常运行,需要在程序开头加上这句代码;error_reporting()设置PHP的报错级别并返回当前的级别,以下是网络上查找的一些资料,在做Excel导出的时候出现了一个bug很久都没有解决,在TP5的交流群询问之后被某位大佬问到:“你知道PHP的报错等级吗?你知道什么是未定义变量吗?”查看错误报告之后发现确实出现了下面的第7条错误:

    1. 错误报告是按位的。或者将数字加起来得到想要的错误报告等级
    2. E_ALL - 所有的错误和警告
    3. E_ERROR - 致命性运行时错
    4. E_WARNING - 运行时警告(非致命性错)
    5. E_PARSE - 编译时解析错误
    6. E_NOTICE - 运行时提醒
    7. 可能是有意的行为造成的(如:基于未初始化的变量自动初始化为一个空字符串的事实而使用一个未初始化的变量)
    8. E_CORE_ERROR - 发生于PHP启动时初始化过程中的致命错误
    9. E_CORE_WARNING - 发生于PHP启动时初始化过程中的警告(非致命性错)
    10. E_COMPILE_ERROR - 编译时致命性错
    11. E_COMPILE_WARNING - 编译时警告(非致命性错)
    12. E_USER_ERROR - 用户产生的出错消息
    13. E_USER_WARNING - 用户产生的警告消息
    14. E_USER_NOTICE - 用户产生的提醒消息
  • 循环处理Excel表格里的每一行数据并插入数据库

      for ($i=3; $i <=$data->sheets[0][\'numRows\'] ; $i++) { 
          	$insert = [];
          	$insert[\'grade\'] = $data->sheets[0][\'cells\'][$i][1];
          	$insert[\'serialNum\'] = $data->sheets[0][\'cells\'][$i][2];
          	$insert[\'password\'] = $data->sheets[0][\'cells\'][$i][2];
          	$insert[\'name\'] = $data->sheets[0][\'cells\'][$i][3];
          	$insert[\'gender\'] = $data->sheets[0][\'cells\'][$i][4];
          	$insert[\'college\'] = $data->sheets[0][\'cells\'][$i][5];
          	$insert[\'department\'] = $data->sheets[0][\'cells\'][$i][6];
          	$insert[\'gpa\'] = $data->sheets[0][\'cells\'][$i][7];
          	$insert[\'rank\'] = $data->sheets[0][\'cells\'][$i][8];
          	$insert[\'telephone\'] = $data->sheets[0][\'cells\'][$i][9];
          	$insert[\'chosen\'] = 0;
          	//插入数据库中
          	Db(\'user_student_\'.$insert[\'grade\'])->insert($insert);
          }
    

三、Excel导出

  • 引入PHPExcel.php

      require_once \'extend/PHPExcel_1.8.0_doc/Classes/PHPExcel.php\';
    
  • 创建一个新的Excel文件

      $excel = new \PHPExcel();
    
  • 先进行一般的Excel格式的处理

      $excel->getActiveSheet()->getColumnDimension(\'A\')->setWidth(9);  //手动设置单元格宽度
      $excel->getActiveSheet()->getColumnDimension(\'B\')->setWidth(30);
      $excel->getActiveSheet()->getColumnDimension(\'C\')->setWidth(9);
      
      $excel->getActiveSheet()->getRowDimension(2)->setRowHeight(35);  //设置某一行高度
      
      $excel->getActiveSheet()->getStyle(\'A1\')->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_CENTER);  //设置水平居中
      $excel->getActiveSheet()->getStyle(\'A1\')->getAlignment()->setVertical(\PHPExcel_Style_Alignment::VERTICAL_CENTER);  //设置水平居中
      
      $excel->getActiveSheet()->getStyle(\'G\')->getNumberFormat()->setFormatCode(\'000000000\');  //设置文本格式
      
      //设置边框和水平垂直居中,PHPExcel貌似没有对所有的单元格进行统一处理的功能,所以我定义里一个$styleArray,方便在往Excel中写入数据时,同时对单元格进行格式的设置
      $styleArray = [ 
          \'alignment\' => [
              \'horizontal\' => \PHPExcel_Style_Alignment::HORIZONTAL_CENTER,
              \'vertical\' => \PHPExcel_Style_Alignment::VERTICAL_CENTER
          ],
          \'borders\' => [
              \'allborders\' => [
                  \'style\' => \PHPExcel_Style_Border::BORDER_THIN
              ]
          ]
      ];
      $excel->getActiveSheet()->getStyle(\'A1\')->applyFromArray($styleArray);
      
      $excel->getActiveSheet()->getStyle(\'A1\')->getFont()->setBold(true);  //设置字体加粗
      
      $excel->getActiveSheet()->mergeCells(\'A1:H1\');  //合并A1:F1单元格
    
  • 对表格第一行标题的特殊处理

      $excel->getActiveSheet()->mergeCells(\'A1:H1\');  //合并A1:H1单元格
      $excel->getActiveSheet()->setCellValue(\'A1\',$insert[0][\'grade\'].\'级\'.$insert[0][\'dep\'].\'导师分配结果\');
      $excel->getActiveSheet()->getStyle(\'A1\')->getFont()->setBold(true);  //加粗
      $excel->getActiveSheet()->getStyle(\'A1\')->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_CENTER); //设置水平居中
      $excel->getActiveSheet()->getStyle(\'A1\')->getAlignment()->setVertical(\PHPExcel_Style_Alignment::VERTICAL_CENTER);     //设置垂直居中
    
  • 对表的标题行的特殊处理

      $letter = [\'A\',\'B\',\'C\',\'D\',\'E\',\'F\',\'G\',\'H\'];
      $tableHeader = [\'序号\',\'系别\',\'专业导师\',\'职称\',\'课题\',\'学生姓名\',\'学号\',\'联系方式\'];    //设置表头数组,单独处理
      for ($i=0; $i <8 ; $i++) { 
          $excel->getActiveSheet()->setCellValue($letter[$i].\'2\',$tableHeader[$i]);            //设置单元格的值
          $excel->getActiveSheet()->getStyle($letter[$i].\'2\')->applyFromArray($styleArray);    //设置单元格格式:水平、垂直居中、加边框
          $excel->getActiveSheet()->getStyle($letter[$i].\'2\')->getFont()->setBold(true);       //设置单元格字体加粗
      }
    
  • 在这次项目中碰到的比较奇葩的问题和处理办法 - 某些单元格的动态合并:

    第一种情况如上图,对于上图这种格式的Excel写入是比较简单的,只需从数据库中取出数据,逐条的插入进表格中,并作格式处理就好了

      $totalInsert = count($insert);   //计算总插入数
      for ($i=0; $i <$totalInsert ; $i++) { 
          $excel->getActiveSheet()->setCellValue(\'A\'.($i+3),($i+1));
          $excel->getActiveSheet()->setCellValue(\'B\'.($i+3),$insert[$i][\'tdep\']);
          $excel->getActiveSheet()->setCellValue(\'C\'.($i+3),$insert[$i][\'sname\']);
          $excel->getActiveSheet()->setCellValue(\'D\'.($i+3),$insert[$i][\'snum\']);
          $excel->getActiveSheet()->setCellValue(\'E\'.($i+3),$insert[$i][\'stele\']);
          $excel->getActiveSheet()->setCellValue(\'F\'.($i+3),$insert[$i][\'tname\']);
          $excel->getActiveSheet()->setCellValue(\'G\'.($i+3),$insert[$i][\'tposi\']);
          $excel->getActiveSheet()->setCellValue(\'H\'.($i+3),$insert[$i][\'title\']);
          $excel->getActiveSheet()->setCellValue(\'I\'.($i+3),$insert[$i][\'ttele\']);
      }
    
      for ($j=0; $j <9 ; $j++) { 
              $excel->getActiveSheet()->getStyle($letter[$j].\'3\'.\':\'.$letter[$j].($totalInsert+2))->applyFromArray($styleArray); //设置单元格格式:水平、垂直居中、加边框
          }
    

    第二种情况如上图,对于初学PHPExcel的我来说,上图的操作简直是麻烦,我的做法是

    1. 先设置两个临时变量 $oldTemp$newTemp 用来处理合并的单元格的范围,例如合并A1:A6,$oldTemp 用来记录合并的左范围,$newTemp用来记录合并的右范围,并且 $newTemp = $oldTemp + 每个导师的学生数,若学生数为0,则加1

    2. 先插入列F、G、H的数据,加样式,然后对前面的列A、B、C、D、E先插值,再进行单元格合并,因为比如A1:A3合并,合并后的单元格名称仍然为A1,合并的范围为 $oldTemp : $newTemp-1,然后交换 $oldTemp$newTemp

    3. 不知道有没有更好的处理办法,对自己的处理办法表示有点愚蠢。。

       $oldTemp = 3;           //临时变量,用于处理合并单元格的范围
       $newTemp = 0;           //临时变量,用于处理合并单元格的范围
      
       for ($i=0; $i <$totalInsert ; $i++) {                 //循环插入数据,并作格式处理
       	if ($insert[$i][\'stuNum\'] == 0) {                 //判断导师是否有学生,如果没有学生,只需插入一行 
           	$tempCount = $insert[$i][\'stuNum\'] + 1;
       	} else {
           	$tempCount = $insert[$i][\'stuNum\'];
       	}
       	for ($j=0; $j <$tempCount ; $j++) {               //开始插入
           	if ($insert[$i][\'stuNum\'] != 0) {             //逐一插入导师的学生信息
               	$excel->getActiveSheet()->setCellValue(\'F\'.($j+$oldTemp),$insert[$i][\'tstudentL\'][$j][\'sname\']);
               	$excel->getActiveSheet()->setCellValue(\'G\'.($j+$oldTemp),$insert[$i][\'tstudentL\'][$j][\'snum\']);
               	$excel->getActiveSheet()->setCellValue(\'H\'.($j+$oldTemp),$insert[$i][\'tstudentL\'][$j][\'stele\']);
           }
      
           	$excel->getActiveSheet()->getStyle(\'F\'.($j+$oldTemp))->applyFromArray($styleArray); //设置单元格格式:水平、垂直居中、加边框
           	$excel->getActiveSheet()->getStyle(\'G\'.($j+$oldTemp))->applyFromArray($styleArray); //设置单元格格式:水平、垂直居中、加边框
           	$excel->getActiveSheet()->getStyle(\'H\'.($j+$oldTemp))->applyFromArray($styleArray); //设置单元格格式:水平、垂直居中、加边框
       }
      
       	$excel->getActiveSheet()->setCellValue(\'A\'.$oldTemp,($i+1));                            //设置单元格的值
       	$excel->getActiveSheet()->setCellValue(\'B\'.$oldTemp,$insert[$i][\'dep\']);
       	$excel->getActiveSheet()->setCellValue(\'C\'.$oldTemp,$insert[$i][\'tname\']);
       	$excel->getActiveSheet()->setCellValue(\'D\'.$oldTemp,$insert[$i][\'position\']);
       	$excel->getActiveSheet()->setCellValue(\'E\'.$oldTemp,$insert[$i][\'title\']);
      
       	$newTemp = $oldTemp + $tempCount;
       	if ($insert[$i][\'stuNum\'] != 1 && $insert[$i][\'stuNum\'] != 0) {
           	for ($k=0; $k <5 ; $k++) { 
               	$excel->getActiveSheet()->mergeCells($letter[$k].$oldTemp.\':\'.$letter[$k].($newTemp-1)); //根据导师的学生数合并A、B、C、D、E列的单元格
           	}
       	}
      
       	for ($z=0; $z <5 ; $z++) { 
           	$excel->getActiveSheet()->getStyle($letter[$z].$oldTemp.\':\'.$letter[$z].($newTemp-1))->applyFromArray($styleArray); //设置单元格格式:水平、垂直居中、加边框
       	}
      
       	$oldTemp = $newTemp;
       }
      
  • 直接输出至浏览器,即下载至本地,只需直接加入代码就行

      $write = new \PHPExcel_Writer_Excel5($excel);
      header("Pragma: public");
      header("Expires: 0");
      header("Cache-Control:must-revalidate, post-check=0, pre-check=0");
      header("Content-Type:application/force-download");
      header("Content-Type:application/vnd.ms-execl");
      header("Content-Type:application/octet-stream");
      header("Content-Type:application/download");;
      header(\'Content-Disposition:attachment;filename=\'.\'"\'.$insert[0][\'grade\'].\'级导师互选结果.xls"\'); //可以对文件名进行处理
      header("Content-Transfer-Encoding:binary");
      $write->save(\'php://output\');
    

四、实践效果动图

学生Excel导入:

Excel模版导出:

结果导出:

五、总结

  • 期间遇到了很多的bug,不断的上网找资料、找博客,学到了很多的知识,比如在一个\'\'上刚了非常多的时间,最后找到错误的时候又喜又气的,还有在往Excel中写数据的时候,碰到一个未定义数组下标[0]的错误的时候,花了更多的时间,反反复复的检查代码愣是没发现错误在哪里,躺在床上没解决bug不甘心又下床苦寻,最后发现数据库中的数据有一些是空的,直接插入会出错,要做一些相应的处理

  • 学到了新知识心里是惊喜的,不过我也是得去做下编译实验的。。

分类:

技术点:

相关文章: