写点什么

phpOffice 实现 Excel 表格导入的解耦方法

作者:北桥苏
  • 2023-05-12
    广东
  • 本文字数:3463 字

    阅读完需:约 11 分钟

前言:

​ 在业务中开发中,表格的导入导出功能很常见。但是这里主要是使用 PhpOffice 类库介绍实现导入表格数据的功能。

冲突:

​ 大部分的导入功能,就是通过点击按钮上传一张表格,然后后台读取表格数据根据业务整理后直接插入到数据库,最后再返回给前端。但是如果表格数据庞大,业务逻辑复杂的时候,就会导致导入那一块很臃肿不好维护。

解决方法:

​ 处理方式是把导入与业务数据插入分离,所以在二者之间添加一个队列就可以了。导入只负责将表格数据存入队列。业务部分可以是单独的系统,最后就是消费队列中的数据了。这样一来,不但提升了导入速度,而且还让导入与系统解耦,不会因为异常而影响到其他业务。

编码:

\1. 下载 PhpOffice。


composer repuire phpoffice/phpspreadsheet
复制代码


\2. 导入导出代码。


<?phpnamespace app\common\helper;
use PhpOffice\PhpSpreadsheet\Spreadsheet;use PhpOffice\PhpSpreadsheet\Writer\Xlsx;use PhpOffice\PhpSpreadsheet\IOFactory;use PhpOffice\PhpSpreadsheet\Cell\Coordinate;use think\Exception;

class Excel{ // 导出 public function outPut($data, $columns, $table = '导出文件') { $spreadsheet = new Spreadsheet(); $sheet = $spreadsheet->getActiveSheet();
// 设置第一栏的标题 foreach ($columns as $k => $v) { $sheet->setCellValue($k . "1", $v['title']); }
//第二行起 设置内容 $baseRow = 2; //数据从N-1行开始往下输出 这里是避免头信息被覆盖
foreach ($data as $key => $value) { foreach ($columns as $k1 => $v1) { $i = $key + $baseRow; $sheet->setCellValue($k1 . $i, $value[$v1['field']]); } }
$writer = new Xlsx($spreadsheet);
$filename = $table . date("Y-m-d", time()) . '_' . time() . '.xlsx';
$writer->save('./excel/' . $filename);
return '/excel/' . $filename; }
// 导入 public function importExcel($file = '', $sheet = 0, $columnCnt = 0, &$options = []) { try { $file = iconv("utf-8", "gb2312", $file);
if (empty($file) OR !file_exists($file)) { throw new \Exception('文件不存在!'); }
$objRead = IOFactory::createReader('Xlsx');
if (!$objRead->canRead($file)) { $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); //$currSheet = $obj->getSheetByName($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; } } }
复制代码


\3. 抽取指定的字段格式化 Excel 数据。


return [
// 导入的表格标题 "bidding" => [ "stock_no" => "编号", "price" => "价格", "mobile" => "手机", "nickname" => "姓名" ]
];


// 格式化指定列数据(默认第一行表头)public static function formattingCells(array $data, array $cellConfig){ $res = array_values($data);
// 表头 $header = $res[0];
$cellKeys = []; foreach ($header as $key => $value) { foreach ($cellConfig as $k => $v) { if ($value == $v) { $cellKeys[$key] = $k; } } }
if (count($cellKeys) != count($cellConfig)) { throw new Exception('表格不完整'); }
// 需要添加过滤 $temp = []; for ($i = 1; $i <= count($res) - 1; $i++) { foreach ($cellKeys as $m => $n) { $temp[$i][$n] = $res[$i][$m]; } }
return array_values($temp);}
复制代码


\4. 导入部分,上传接口。


// 导入表格,上传接口public function importExcel(){    $upload_file = $_FILES['files']['tmp_name'];    $input = $this->input;
// ID $id = isset($input['id']) ? $input['id'] : 0;
// 默认取第一工作表 $excelData = (new Excel())->importExcel($upload_file, 0);
// 取Excel字段 $config = config('excel_export.bidding');
$price_offer = Excel::formattingCells($excelData, $config);
// 判断每条记录的手机和价格格式 // ……
$jsonList = json_encode(compact('id', 'price_offer')); //$jsonList = json_encode($price_offer);
// 入MQ $host = config("mq.host"); $options = config("mq.price_offer_import");
try { $mq = new ProductMQ($host, $options);
$mq->publish($jsonList);
$mq->close();
} catch (\Exception $e) { return $this->jsonData(200, $e->getMessage()); } // 入MQ
return $this->jsonData(200, '导入成功');}
复制代码


\5. 消费业务逻辑。




用户头像

北桥苏

关注

公众号:ZERO开发 2023-05-08 加入

专注后端实战技术分享,不限于PHP,Python,JavaScript, Java等语言,致力于给猿友们提供有价值,有干货的内容。

评论

发布
暂无评论
phpOffice实现Excel表格导入的解耦方法_php_北桥苏_InfoQ写作社区