咔片PPT · AI自动生成演示文稿,模板丰富、排版精美 讯飞智文 · 一键生成PPT和Word,高效应对学习与办公

看过我之前分享的朋友,应该知道之前我用 ,需要导入大量的数据,也就研究了一下如何在后台批量导入大量的数据。因为源数据都是存储在excel文件内而且数据结构相对比较复杂,所以一般CSV的导入方式就有点力不从心了。在网上找到不少前后端的解决方案,不过后端方案里面将excel导入mysql数据的方法主要是使用Python,还有一些是直接使用mysql可视化工具来解决,例如使用navicat来导入。不过因为这次的数据结构有一些特殊,需要一个可编程化的解决方案来定制一下,所以解决方案就更有限了。最终,尝试了两个不同的PHP库,一个是使用spreadsheet-reader库,一个是使用PhpSpreadsheet,在最终几次尝试后,还是选择功能更强大的PhpSpreadsheet。

PhpSpreadsheet

PhpSpreadsheet是什么

PhpSpreadsheet是一个纯以PHP编写的库,通过提供的类来处理不同类型的电子表格文件读写功能,而且PhpSpreadsheet支持的电子表格格式也算是比较丰富,而且对于spreadsheet-reader专注于数据读取最大的区别是同时支持读取和写入的功能。如下是PhpSpreadsheet支持的电子表格格式,虽然还有一些不支持写入,但是主流文件格式全都支持的:

PhpSpreadsheet支持的电子表格类型

PhpSpreadsheet的环境支持和安装

PhpSpreadsheet对于PHP的版本要求要略高,需要7.2以上的版本,相对于spreadsheet-reader的PHP 5.3的版本要求来说,可能还需要对于服务器的PHP版本做个更新,不过PHP7的确提供了不少不错的功能升级,所以将PHP版本更新成7甚至更新的8还是不错的选择。

安装PhpSpreadsheet

可以直接通过Composer来安装PhpSpreadsheet,可以一步将PhpSpreadsheet的依赖库一起管理起来,直接composer require就可以了:

composer require phpoffice/phpspreadsheet

先来一个Hello World:

getActiveSheet(); $sheet->setCellValue('A1', 'Hello World !'); $writer = new Xlsx($spreadsheet); $writer->save('hello world.xlsx'); ?>

直接执行这个PHP之后就会在相同的文件夹中生成一个hello world.xlsx了,比较建议直接使用PHP的命令行来执行

php path/to/helloworld.php

使用方法

加载excel文件

在PhpSpreadsheet需要通过IOFactory的静态方法createReader来创建一个阅读器的对象,在创建的时候需要设置阅读文件的格式,然后直接通过阅读器对象的load方法就可以加载需要的文件了,例如下面是一个加载xlsx文件的代码:

$inputFileType = 'Xlsx'; // $inputFileType = 'Xls'; // $inputFileType = 'Xml'; // $inputFileType = 'Ods'; // $inputFileType = 'Slk'; // $inputFileType = 'Gnumeric'; // $inputFileType = 'Csv'; $inputFileName = './sampleData/example1.xls'; /** 根据$inputFileType生成一个阅读器对象 **/ $reader = PhpOfficePhpSpreadsheetIOFactory::createReader($inputFileType); /** 加载 $inputFileName 并生成一个Spreadsheet对象 **/ $spreadsheet = $reader->load($inputFileName);

当然也可以用更直接的方式加载文件,就是通过IOFactory的静态方法load,这个方法会尝试识别文件类型,然后加载相对应的阅读器实例:

$inputFileName = './sampleData/example1.xls'; /** 加载 $inputFileName 并生成一个Spreadsheet对象 **/ $spreadsheet = PhpOfficePhpSpreadsheetIOFactory::load($inputFileName);

还有一种方式,就是直接通过new来创建阅读器对象,然后在通过阅读器的load方法加载文件,如下:

$inputFileName = './sampleData/example1.xls'; /** 创建一个Xlsx阅读器对象 **/ $reader = new PhpOfficePhpSpreadsheetReaderXlsx(); // $reader = new PhpOfficePhpSpreadsheetReaderXls(); // $reader = new PhpOfficePhpSpreadsheetReaderXml(); // $reader = new PhpOfficePhpSpreadsheetReaderOds(); // $reader = new PhpOfficePhpSpreadsheetReaderSlk(); // $reader = new PhpOfficePhpSpreadsheetReaderGnumeric(); // $reader = new PhpOfficePhpSpreadsheetReaderCsv(); /** 加载 $inputFileName 并生成一个Spreadsheet对象 **/ $spreadsheet = $reader->load($inputFileName);

PhpSpreadsheet还提供了一些阅读器的设置,例如设置文件数据只读,只加载制定工作表等,具体可以参考文档

其中Spreadsheet对象就是电子表格文件相对应的可操作对象,在之后可以直接调用这个对象中方法来完成不同的目的。

获取相应的工作表

首先对于包含多个工作表的电子表格文件,可以先获取到工作表的数量,然后在通过循环来遍历所有的工作表,当然也可以根据工作表标题来加载制定工作表,或者是获取当前活跃的工作表,也就是用Excel打开电子表格时,当前显示的工作表。

/** 获取工作表数量 **/ $sheetCount = $spreadsheet->getSheetCount(); for($i=0; $i<$sheetCount; $i++) { /** 根据下标来获取对应的工作表 **/ $sheet = $spreadsheet->getSheet($i); /** 对每个工作表的操作 **/ } /** 根据工作表名称获取工作表 **/ $sheet = $spreadsheet->getSheetByName('Sheet1'); /** 直接获取活动的工作表 **/ $sheet = $spreadsheet->getActiveSheet();

读取单元格数据

当获取到Sheet对象之后,就可以获取工作表中每一个单元格的对象,最终获取单元格内的数据。因为我这里主要是大量数据的导入,所以我首先使用的是遍历所有有数据的单元格以完成所有的数据导入,要遍历整个工作表内的数据,首先需要获取工作表中总共有多少列和多少行数据,可以通过如下的代码:

// 获取相应工作表内,最高的行数和列数 $highestRow = $sheet->getHighestRow(); // 如: 10 $highestColumn = $sheet->getHighestColumn(); // 如: 'F' // 最后需要将列数转换成数字,因为绝大多数电子表格列数是用字母来表示的 $highestColumnIndex = PhpOfficePhpSpreadsheetCellCoordinate::columnIndexFromString($highestColumn); // 如 5

有了最高的行数和列数之后,就可以用一个简单的嵌套For循环来遍历整个工作表内的每个单元格:

for($row = 1;$row<=$highestRow;$row++) { for($col = 1;$col<=$highestColumnIndex;$col++) { /** 获取单元格的对象 **/ $cell = $sheet->getCellByColumnAndRow($col,$row); /** 获取单元格的数值 **/ $value = $cell->getValue(); /** 也可以是将方法串联起来 **/ // $value = $sheet->getCellByColumnAndRow($col,$row)->getValue(); /** 如果数据是通过一个公式得出的话,可以使用getCalculatedValue() **/ // $calculatedValue = $sheet->getCellByColumnAndRow($col,$row)->getCalculatedValue(); /** 如果需要格式化相应单元格内的数据,例如日期等,可以使用getFormattedValue() **/ // $formatedValue = $sheet->getCellByColumnAndRow($col,$row)->getFormattedValue(); } }

基于习惯我是直接使用下标的方式来遍历这个工作表中的单元格,实际PhpSpreadsheet还提供了一种通过迭代器(Iterator)遍历的方式,这样代码可以更简洁一些,代码如下:

foreach($sheet->getRowIterator() as $row) { $cellIterator = $row->getCellIterator(); /** 设置遍历所有单元格,就算单元格是空的 **/ $cellIterator->setIterateOnlyExistingCell(FALSE); foreach($cellIterator as $cell) { $value = $cell->getValue(); } }

对于其他的应用场景,可能直接通过工作表内的坐标值或者行列对来获取一个单元格的对象会更直接一些,例如:

/** 获取坐标为A1的单元格内的值 **/ $value=$sheet->getCell('A1')->getValue(); /** 获取第1列 第5行的单元格内的值 **/ $value=$sheet->getCellByColumnAndRow(1,5)->getValue();

最后一种方式可以通过一个范围来获取范围内单元格的值,然后输出到数组,如下:

/** 获取C3至E5内的数据 $dataArray = $sheet->rangeToArray( 'C3:E5', // 工作表内的单元格坐标范围 NULL, // 当单元格为空时,返回的值 TRUE, // 如果是公式,是否要计算值,等同使用getCalcuatedValue() TRUE, // 是否需要使用格式化的数据,等同使用getFormattedValue() TRUE // 输出数组是否使用表格单元格的行数和列数作为下标 );

数据库操作

因为这次读入的数据需要插入相应的数据库中,而且每个工作表中的数据列所代表的属性也不同,所以还需要在表格的第一行加入相应属性的名称,也就是在遍历整个工作表的时候,当处于第一行的时候,需要将此行内单元格的内容保存为键,然后在之后的行数中就可以用相应的列数来获取对应的键来添加入数据的数组中,最终在每一行数据读取完成后,来处理数据库插入的操作。当然,如果导入数据结构相对比较直观的话,也可以通过一个insert语句导入,这样可以大幅度提升数据写入的效率。

总结

这次主要是分享了PhpSpreadsheet数据读取的部分,也是这次在我自己项目中用到的部分,总之PhpSpreadsheet的功能还是比较完善的,文档也是非常完善的还包含了不少示例,只是目前核心文档还是英文的,中文文档相对比较零散。如果大家感兴趣了解更多关于PhpSpreadsheet的其他功能,欢迎在评论区讨论!

如果大家对PhpSpreadsheet 或者其他开发方面的话题感兴趣的话,可以关注我!谢谢阅读!