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


    前言:    

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

    冲突:

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

    解决方法:

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

    编码:

    1. 下载PhpOffice。

    composer repuire phpoffice/phpspreadsheet

    2. 导入导出代码。

    <?php
    namespace appcommonhelper;
    
    use PhpOfficePhpSpreadsheetSpreadsheet;
    use PhpOfficePhpSpreadsheetWriterXlsx;
    use PhpOfficePhpSpreadsheetIOFactory;
    use PhpOfficePhpSpreadsheetCellCoordinate;
    use thinkException;
    
    
    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. 消费业务逻辑。

  • 相关阅读:
    基于python创建一个简单的HTTP-WEB服务器
    基于python自动化测试平台与虚拟化技术结合的思考
    Net分布式系统之六:微服务之API网关
    Net分布式系统之五:C#使用Redis集群缓存
    Net分布式系统之二:CentOS系统搭建Nginx负载均衡
    Net分布式系统之一:系统整体框架介绍
    .Net微服务架构之运行日志分析系统
    Net分布式系统之七:日志采集系统(1)
    程序员如何选择未来的职业路线
    NET技术公众号已上线
  • 原文地址:https://www.cnblogs.com/zerofc/p/14154799.html
Copyright © 2020-2023  润新知