codeigniter3+PHPspreadsheet實現excel導入導出功能
引入composer依賴包PHPspreadsheet
- 根目錄composer.json文件的require節點加入如下內容,注意json格式規則:
"phpoffice/phpspreadsheet": "*"
- 之後需執行 composer update 下載PHPspreadsheet依賴包
添加類庫並use相關類
- 在 application\libraries 目錄下添加名爲 Php_spread_sheet_lib.php 的文件(名稱隨意,注意不要使用PHPspreadsheet,避免不必要的衝突,ci3無命名空間)
- use必要類
use PhpOffice\PhpSpreadsheet\Cell\Coordinate; use PhpOffice\PhpSpreadsheet\Cell\DataType; use PhpOffice\PhpSpreadsheet\IOFactory; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Style\Alignment; use PhpOffice\PhpSpreadsheet\Style\Border; use PhpOffice\PhpSpreadsheet\Style\Color; use PhpOffice\PhpSpreadsheet\Style\Fill; use PhpOffice\PhpSpreadsheet\Style\NumberFormat; use PhpOffice\PhpSpreadsheet\Worksheet\PageSetup; use PhpOffice\PhpSpreadsheet\Reader\Xlsx; use PhpOffice\PhpSpreadsheet\Reader\Xls;
添加導入導出方法
- 類文件主要內容如下
class Php_spread_sheet_lib { /** * 使用PHPEXECL導入 * * @param string $file 文件地址 * @param int $sheet 工作表sheet(傳0則獲取第一個sheet) * @param int $columnCnt 列數(傳0則自動獲取最大列) * @param array $options 操作選項 * array mergeCells 合併單元格數組 * array formula 公式數組 * array format 單元格格式數組 * * @return array * @throws Exception */ public function importExecl(string $file = '', int $sheet = 0, int $columnCnt = 0, &$options = []) { try { /* 轉碼 */ $file = iconv("utf-8", "gb2312", $file); if (empty($file) OR !file_exists($file)) { throw new \Exception('文件不存在!'); } /** @var Xlsx $objRead */ $objRead = IOFactory::createReader('Xlsx'); if (!$objRead->canRead($file)) { /** @var Xls $objRead */ $objRead = IOFactory::createReader('Xls'); if (!$objRead->canRead($file)) { throw new \Exception('只支持導入Excel文件!'); } } /* 如果不需要獲取特殊操作,則只讀內容,可以大幅度提升讀取Excel效率 */ empty($options) && $objRead->setReadDataOnly(true); /* 建立excel對象 */ $obj = $objRead->load($file); /* 獲取指定的sheet表 */ $currSheet = $obj->getSheet($sheet); if (isset($options['mergeCells'])) { /* 讀取合併行列 */ $options['mergeCells'] = $currSheet->getMergeCells(); } if (0 == $columnCnt) { /* 取得最大的列號 */ $columnH = $currSheet->getHighestColumn(); /* 兼容原邏輯,循環時使用的是小於等於 */ $columnCnt = Coordinate::columnIndexFromString($columnH); } /* 獲取總行數 */ $rowCnt = $currSheet->getHighestRow(); $data = []; /* 讀取內容 */ for ($_row = 1; $_row <= $rowCnt; $_row++) { $isNull = true; for ($_column = 1; $_column <= $columnCnt; $_column++) { $cellName = Coordinate::stringFromColumnIndex($_column); $cellId = $cellName . $_row; $cell = $currSheet->getCell($cellId); if (isset($options['format'])) { /* 獲取格式 */ $format = $cell->getStyle()->getNumberFormat()->getFormatCode(); /* 記錄格式 */ $options['format'][$_row][$cellName] = $format; } if (isset($options['formula'])) { /* 獲取公式,公式均爲=號開頭數據 */ $formula = $currSheet->getCell($cellId)->getValue(); if (0 === strpos($formula, '=')) { $options['formula'][$cellName . $_row] = $formula; } } if (isset($format) && 'm/d/yyyy' == $format) { /* 日期格式翻轉處理 */ $cell->getStyle()->getNumberFormat()->setFormatCode('yyyy/mm/dd'); } $data[$_row][$cellName] = trim($currSheet->getCell($cellId)->getFormattedValue()); if (!empty($data[$_row][$cellName])) { $isNull = false; } } /* 判斷是否整行數據爲空,是的話刪除該行數據 */ if ($isNull) { unset($data[$_row]); } } return $data; } catch (\Exception $e) { throw $e; } } /** * Excel導出,TODO 可繼續優化 * * @param array $datas 導出數據,格式['A1' => 'XXXX公司報表', 'B1' => '序號'] * @param string $fileName 導出文件名稱 * @param array $options 操作選項,例如: * bool print 設置打印格式 * string freezePane 鎖定行數,例如表頭爲第一行,則鎖定表頭輸入A2 * array setARGB 設置背景色,例如['FFc0c0c0'=>'A1', 'FF000000'=>['C1', 'C2']] 鍵爲FF開頭的顏色值,值爲單元格可以爲單個字符串或多個組成的數組,說明多個單元格都適用同一種顏色背景 * array setWidth 設置寬度,例如['A' => 30, 'C' => 20] * array setHeight 設置高度,例如['1' => 300, '10' => 200]//鍵爲行號,值爲行高 * array setWrap 設置換行,例如['A1', 'C2'] * 要實現單元格換行,包括兩部分:首先,需要換行的內容之間必需包括換行符,可以用PHP_EOL或者\r\n;其次,必需激活單元格的“自動換行”屬性 * bool setBorder 設置單元格邊框 * array mergeCells 設置合併單元格,例如['A1:J1' => 'A1:J1'] * array formula 設置公式,例如['F2' => '=IF(D2>0,E42/D2,0)'] * array format 設置格式,整列設置,例如['A' => 'General'] * array alignCenter 設置居中樣式,例如['A1', 'A2'] * array bold 設置加粗樣式,例如['A1', 'A2'] * string savePath 保存路徑,設置後則文件保存到服務器,不通過瀏覽器下載 * @param string $excelType 導出文件格式類型,默認xlsx,可選xls和xlsx * * @return bool 成返回true,失敗返回false * @throws Exception */ function exportExcel(array $datas, string $fileName = '', array $options = [], string $excelType='xlsx'): bool { try { if (empty($datas)) { return false; } set_time_limit(0); /** @var Spreadsheet $objSpreadsheet */ //$objSpreadsheet = app(Spreadsheet::class);//laravel寫法 $objSpreadsheet = new Spreadsheet(); /* 設置默認文字居左,上下居中 */ $styleArray = [ 'alignment' => [ 'horizontal' => Alignment::HORIZONTAL_LEFT, 'vertical' => Alignment::VERTICAL_CENTER, ], ]; $objSpreadsheet->getDefaultStyle()->applyFromArray($styleArray); /* 設置Excel Sheet */ $activeSheet = $objSpreadsheet->setActiveSheetIndex(0); /* 打印設置 */ if (isset($options['print']) && $options['print']) { /* 設置打印爲A4效果 */ $activeSheet->getPageSetup()->setPaperSize(PageSetup:: PAPERSIZE_A4); /* 設置打印時邊距 */ $pValue = 1 / 2.54; $activeSheet->getPageMargins()->setTop($pValue / 2); $activeSheet->getPageMargins()->setBottom($pValue * 2); $activeSheet->getPageMargins()->setLeft($pValue / 2); $activeSheet->getPageMargins()->setRight($pValue / 2); } /* 行數據處理 */ foreach ($datas as $sKey => $sItem) { /* 默認文本格式 */ $pDataType = DataType::TYPE_STRING; /* 設置單元格格式 */ if (isset($options['format']) && !empty($options['format'])) { $colRow = Coordinate::coordinateFromString($sKey); /* 存在該列格式並且有特殊格式 */ if (isset($options['format'][$colRow[0]]) && NumberFormat::FORMAT_GENERAL != $options['format'][$colRow[0]]) { $activeSheet->getStyle($sKey)->getNumberFormat() ->setFormatCode($options['format'][$colRow[0]]); if (false !== strpos($options['format'][$colRow[0]], '0.00') && is_numeric(str_replace(['¥', ','], '', $sItem))) { /* 數字格式轉換爲數字單元格 */ $pDataType = DataType::TYPE_NUMERIC; $sItem = str_replace(['¥', ','], '', $sItem); } } elseif (is_int($sItem)) { $pDataType = DataType::TYPE_NUMERIC; } } $activeSheet->setCellValueExplicit($sKey, $sItem, $pDataType); /* 存在:形式的合併行列,列入A1:B2,則對應合併 */ if (false !== strstr($sKey, ":")) { $options['mergeCells'][$sKey] = $sKey; } } unset($datas); /* 設置鎖定行 */ if (isset($options['freezePane']) && !empty($options['freezePane'])) { $activeSheet->freezePane($options['freezePane']); unset($options['freezePane']); } /* 設置寬度 */ if (isset($options['setWidth']) && !empty($options['setWidth'])) { foreach ($options['setWidth'] as $swKey => $swItem) { if(is_numeric($swItem)){//設置寬度 $activeSheet->getColumnDimension($swKey)->setWidth($swItem); }else{//自動寬度 $activeSheet->getColumnDimension($swKey)->setAutoSize($swItem); } } unset($options['setWidth']); } /* 設置行高度 */ if (isset($options['setHeight']) && !empty($options['setHeight'])) { foreach ($options['setHeight'] as $shKey => $shItem) { if(is_numeric($shItem)){//設置寬度 $activeSheet->getRowDimension($shKey)->setRowHeight($shItem); }else{//默認高度15 $activeSheet->getDefaultRowDimension()->setRowHeight(15); } } unset($options['setHeight']); } /* 設置換行 */ if (isset($options['setWrap']) && !empty($options['setWrap'])) { foreach ($options['setWrap'] as $swItem) { $activeSheet->getStyle($swItem)->getAlignment()->setWrapText(true); } unset($options['setWrap']); } /* 設置背景色 */ if (isset($options['setARGB']) && !empty($options['setARGB'])) { /*foreach ($options['setARGB'] as $sItem) { $activeSheet->getStyle($sItem) ->getFill()->setFillType(Fill::FILL_SOLID) ->getStartColor()->setARGB(Color::COLOR_YELLOW); }*/ foreach ($options['setARGB'] as $sKey => $sItem) { if(!is_array($sItem)){//不是數組 $activeSheet->getStyle($sItem) ->getFill()->setFillType(Fill::FILL_SOLID) ->getStartColor()->setARGB($sKey); }else{//是數組 foreach($sItem as $item){ $activeSheet->getStyle($item) ->getFill()->setFillType(Fill::FILL_SOLID) ->getStartColor()->setARGB($sKey); } } } unset($options['setARGB']); } /* 設置公式 */ if (isset($options['formula']) && !empty($options['formula'])) { foreach ($options['formula'] as $fKey => $fItem) { $activeSheet->setCellValue($fKey, $fItem); } unset($options['formula']); } /* 合併行列處理 */ if (isset($options['mergeCells']) && !empty($options['mergeCells'])) { $activeSheet->setMergeCells($options['mergeCells']); unset($options['mergeCells']); } /* 設置居中 */ if (isset($options['alignCenter']) && !empty($options['alignCenter'])) { $styleArray = [ 'alignment' => [ 'horizontal' => Alignment::HORIZONTAL_CENTER, 'vertical' => Alignment::VERTICAL_CENTER, ], ]; foreach ($options['alignCenter'] as $acItem) { $activeSheet->getStyle($acItem)->applyFromArray($styleArray); } unset($options['alignCenter']); } /* 設置加粗 */ if (isset($options['bold']) && !empty($options['bold'])) { foreach ($options['bold'] as $bItem) { $activeSheet->getStyle($bItem)->getFont()->setBold(true); } unset($options['bold']); } /* 設置單元格邊框,整個表格設置即可,必須在數據填充後纔可以獲取到最大行列 */ if (isset($options['setBorder']) && $options['setBorder']) { $border = [ 'borders' => [ 'allBorders' => [ 'borderStyle' => Border::BORDER_THIN, // 設置border樣式 'color' => ['argb' => 'FF000000'], // 設置border顏色 ], ], ]; $setBorder = 'A1:' . $activeSheet->getHighestColumn() . $activeSheet->getHighestRow(); $activeSheet->getStyle($setBorder)->applyFromArray($border); unset($options['setBorder']); } $fileName = !empty($fileName) ? $fileName.'.'.strtolower($excelType) : (date('YmdHis').'.'.strtolower($excelType)); if (!isset($options['savePath'])) { if($excelType == 'xlsx'){ /* 直接導出Excel,無需保存到本地,輸出07Excel文件 */ header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); }else{ header('Content-Type: application/vnd.ms-excel'); } header( "Content-Disposition:attachment;filename=" . iconv( "utf-8", "GB2312//TRANSLIT", $fileName ) ); header('Cache-Control: max-age=0');//禁止緩存 $savePath = 'php://output'; } else { $savePath = $options['savePath'].$fileName; } ob_clean(); ob_start(); $objWriter = IOFactory::createWriter($objSpreadsheet, ucfirst($excelType)); $objWriter->save($savePath); /* 釋放內存 */ $objSpreadsheet->disconnectWorksheets(); unset($objSpreadsheet); ob_end_flush(); return true; } catch (Exception $e) { return false; } } }
- 下載完整類庫文件傳送門
實戰應用
- 這裏把我項目中的調用方法展現出來,讀着只需看與excel相關的有用的(寬高、樣式、合併單元格等)即可,看懂類庫文件就能會用,我這裏也是對它的運用
/** * 方法 export_project_apply_detail_as_excel_serv,導出項目抽檢申請詳情數據到excel * * @param array $params 參數數組 * * @return mixed 成功返回true,失敗返回false */ public function export_project_apply_detail_as_excel_serv(array $params){ //獲取項目抽檢申請詳情(包含項目地址所屬公司等信息) $project_apply_department = $this->get_project_apply_department_detail_serv($params); //獲取項目抽檢申請檢查項列表 $resolve_item_list = $this->deal_get_project_apply_item_list_serv($params); //處理成可寫入excel的數據格式 $data = [ 'A1' => '公司名稱', 'B1' => '項目名稱', 'C1' => '立項時間', 'D1' => '項目地址', 'E1' => '技術負責人', 'F1' => '手機號', 'A2' => $project_apply_department['company_name'], 'B2' => $project_apply_department['project_name'], 'C2' => $project_apply_department['create_time'], 'D2' => $project_apply_department['address'], 'E2' => $project_apply_department['tec_leader_name'], 'F2' => $project_apply_department['tec_leader_phone'], 'A4' => '項目狀態', 'B4' => '抽籤時間', 'C4' => '預計抽檢時間', 'D4' => '抽檢人員', 'E4' => '職務', 'F4' => '手機號', 'A5' => $project_apply_department['status_name'], 'B5' => $project_apply_department['draw_date'], 'C5' => $project_apply_department['plan_samp_date'], 'D5' => $project_apply_department['samp_user_name'], 'E5' => $project_apply_department['samp_user_job'], 'F5' => $project_apply_department['samp_user_phone'], 'A7' => '分數', 'B7' => '檢查時間', 'A8' => $project_apply_department['score'], 'B8' => $project_apply_department['samp_date'] ?? '暫無', 'A10' => '檢查項', 'B10' => '檢查樓層', ]; //處理抽檢的檢查項樓層數據 static $row = 11; foreach($project_apply_department['project_apply_wbs_item'] as $k => $v){ if($v['plaster']){ $tmp = ['A'.$row => $v['plaster_name'], 'B'.$row => $v['build_name'].$v['floor_name'].'層',]; $data = array_merge($data, $tmp); $row ++; continue; } if($v['concrete']){ $tmp = ['A'.$row => $v['concrete_name'], 'B'.$row => $v['build_name'].$v['floor_name'].'層',]; $data = array_merge($data, $tmp); $row ++; } if($v['masonry']){ $tmp = ['A'.$row => $v['masonry_name'], 'B'.$row => $v['build_name'].$v['floor_name'].'層',]; $data = array_merge($data, $tmp); $row ++; } } //處理檢查項樓層列表數據 $row ++; $bold_row2 = $row;//設置加粗的行號 $tmp2 = ['A'.$row => '樓號', 'B'.$row => '層號', 'C'.$row => '形象進度', 'D'.$row => '檢查項', 'E'.$row => '檢查項', 'F'.$row => '檢查項']; $data = array_merge($data, $tmp2); $row ++; foreach($resolve_item_list as $r_i_l_k => $r_i_l_v){ /*if($r_i_l_v['plaster']){ unset($r_i_l_v['concrete_name'], $r_i_l_v['masonry_name']); }else{ unset($r_i_l_v['plaster_name']); }*/ $tmp3 = [ 'A'.$row => $r_i_l_v['build_name'], 'B'.$row => $r_i_l_v['floor_name'], 'C'.$row => $project_apply_department['appearance_name'], 'D'.$row => $r_i_l_v['concrete_name'] ?? '', 'E'.$row => $r_i_l_v['plaster_name'] ?? '', 'F'.$row => $r_i_l_v['masonry_name'] ?? '', ]; $data = array_merge($data, $tmp3); $row ++; } //設置導出excel單元格格式(居中、加粗等) $oss_path = 's_c_s_l/excel/'; $save_path = getcwd().'/'.$oss_path; $file_name = $project_apply_department['project_name'].'-項目抽檢申請詳情資料'; $file_ext = 'xls'; $options = [ 'print' => true,//打印格式 'alignCenter' => array_keys($data),//居中 'setWidth' => ['A'=>true, 'B'=>true, 'C'=>true, 'D'=>true, 'E'=>true, 'F'=>true], 'bold' => [ 'A1', 'B1', 'C1', 'D1', 'E1', 'F1', 'A4', 'B4', 'C4', 'D4', 'E4', 'F4', 'A7', 'B7', 'C7', 'D7', 'E7', 'F7', 'A10', 'B10', 'A'.$bold_row2, 'B'.$bold_row2, 'C'.$bold_row2, 'D'.$bold_row2, 'E'.$bold_row2, 'F'.$bold_row2, ],//加粗 'savePath' => $save_path,//存儲路徑 ]; //導出,在瀏覽器輸出二進制流主動生成excel文件 $this->load->library('php_spread_sheet_lib'); $export_res = $this->php_spread_sheet_lib->exportExcel($data, $file_name, $options, $file_ext); if($export_res){ $export_res = [ 'oss_path' => $oss_path.$file_name.'.'.$file_ext, 'local_full_path' => $save_path.$file_name.'.'.$file_ext, ]; } return $export_res; }
參考(基於並擴展導出excel的方法):使用PhpSpreadsheet導入&導出Excel(適用各種Excel操作場景)