QQ 咨询
谢谢打赏
微信交流
432

PhpOffice实现Excel表格导入的解耦方法
时间: 2020-12-21 14:54:30  作者:北桥苏  阅读:(5)

前言:    

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

 

冲突:

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

 

解决方法:

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

 

编码:

1. 下载PhpOffice。

composer repuire phpoffice/phpspreadsheet

2. 导入导出代码。


namespace 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. 消费业务逻辑。






用户登录


     QQ登录

我的标签


随机文章

  • 以PHP门面模式实现简单的邮件发送门面模式属于设计模式中三大分类之一的结构类型…… 查看详情
  • 福利:优雅开发的两款小工具 | 今日代码免费开源大家可以自行上网搜索以上两个工具,也可以加Q…… 查看详情
  • PHP应用如何对接微信公众号JSAPI支付 微信支付的产品有很多,1. JSAPI支付 …… 查看详情

友情链接





Copyright© 2016-2020 北桥苏 & 版权所有    赣ICP备16002525号-1
如有侵权、不妥之处,请联系站长的客服并出示版权证明以便删除。敬请谅解! 站长客服联系方式:QQ2652364582