由於畢設導師智能分配系統的須要,系負責人在管理學生和導師時,但願可使用Excel批量導入學生和導師的信息,學長的報課系統使用的是PHPExcel的類庫,因而我也抽空花了2天的時間學習了PHPExcel的最基礎的導入導出功能的實現,總結了本身的使用心得和一些經常使用的使用方法
PHP我使用的是ThinkPHP5的框架,因此以TP5框架爲例,這是項目的PHPExcel的目錄結構:
php
require_once 'extend/reader.php'; //Excel讀取 require_once 'extend/PHPExcel_1.8.0_doc/Classes/PHPExcel.php'; //Excel導入導出
調用Reader數據庫
require_once 'extend/reader.php';
建立Reader數組
$data = new \Spreadsheet_Excel_Reader();
設置在頁面中輸出的編碼方式瀏覽器
$data->setOutputEncoding('utf-8');
讀取上傳到當前目錄下實際路徑爲$realPath的文件網絡
$data->read($realPath);
設置PHP的報錯級別並返回當前的級別app
error_reporting(E_ALL ^ E_NOTICE); 或error_reporting(E_ALL & ~E_NOTICE);
起初並非很懂這句代碼的含義,通過查找資料後發現,在不一樣的PHP版本中,本來可能在低版本的PHP中運行正常的代碼,在較高版本的PHP中運行可能就會出現報錯,爲了使程序可以正常運行,須要在程序開頭加上這句代碼;error_reporting()設置PHP的報錯級別並返回當前的級別,如下是網絡上查找的一些資料,在作Excel導出的時候出現了一個bug好久都沒有解決,在TP5的交流羣詢問以後被某位大佬問到:「你知道PHP的報錯等級嗎?你知道什麼是未定義變量嗎?」查看錯誤報告以後發現確實出現了下面的第7條錯誤:框架
循環處理Excel表格裏的每一行數據並插入數據庫post
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); }
引入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的我來講,上圖的操做簡直是麻煩,個人作法是
先設置兩個臨時變量 $oldTemp
和 $newTemp
用來處理合並的單元格的範圍,例如合併A1:A6,$oldTemp
用來記錄合併的左範圍,$newTemp
用來記錄合併的右範圍,而且 $newTemp = $oldTemp + 每一個導師的學生數
,若學生數爲0,則加1
先插入列F、G、H的數據,加樣式,而後對前面的列A、B、C、D、E先插值,再進行單元格合併,由於好比A1:A3合併,合併後的單元格名稱仍然爲A1,合併的範圍爲 $oldTemp : $newTemp-1
,而後交換 $oldTemp
和 $newTemp
不知道有沒有更好的處理辦法,對本身的處理辦法表示有點愚蠢。。
$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不甘心又下牀苦尋,最後發現數據庫中的數據有一些是空的,直接插入會出錯,要作一些相應的處理
學到了新知識內心是驚喜的,不過我也是得去作下編譯實驗的。。