需求:Excel表格是学校的很多信息,包括基本信息,专业信息等,有多个sheet;需以此表格为依据导入到数据库生成一个学校;或者更新一个学校的信息;
难点: Excel表格中 学校的logo 是图片;是jpg,png,等格式;png格式是 如何正确获取到这张图片。
使用的第三方处理excel表格的库为:
\PhpOffice\PhpSpreadsheet
代码:
public static function importExcelWithImage($filePath, $startRow = 1)
{
$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx();
if (!$reader->canRead($filePath))
{
$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xls();
// setReadDataOnly Set read data only 只读单元格的数据,不格式化 e.g. 读时间会变成一个数据等
if (!$reader->canRead($filePath))
{
throw new NotFoundHttpException('不能读取Excel');
}
}
$spreadsheet = $reader->load($filePath);
$sheetCount = $spreadsheet->getSheetCount();// 获取sheet的数量
// 获取所有的sheet表格数据
$images = [];
$excleDatas = [];
$emptyRowNum = 0;
for ($i = 0; $i < $sheetCount; $i++)
{
$currentSheet = $spreadsheet->getSheet($i); // 读取excel文件中的第一个工作表
$allColumn = $currentSheet->getHighestColumn(); // 取得最大的列号
$allColumn = Coordinate::columnIndexFromString($allColumn); // 由列名转为列数('AB'->28)
$allRow = $currentSheet->getHighestRow(); // 取得一共有多少行
$arr = [];
for ($currentRow = $startRow; $currentRow <= $allRow; $currentRow++)
{
// 从第1列开始输出
for ($currentColumn = 1; $currentColumn <= $allColumn; $currentColumn++)
{
$cellObj = $currentSheet->getCellByColumnAndRow($currentColumn, $currentRow);
$type = $cellObj->getDataType();
$val = $cellObj->getValue();
$arr[$currentRow][] = trim($val);
}
// $arr[$currentRow] = array_filter($arr[$currentRow]);
// 统计连续空行
if (empty($arr[$currentRow]) && $emptyRowNum <= 50)
{
$emptyRowNum++ ;
}
else
{
$emptyRowNum = 0;
}
// 连续50行数据为空,不再读取后面行的数据,防止读满内存
if ($emptyRowNum > 50)
{
break;
}
}
//开始处理图片
foreach ($currentSheet->getDrawingCollection() as $drawing) {
if ($drawing instanceof MemoryDrawing) {
switch ($drawing->getMimeType()) {
case MemoryDrawing::MIMETYPE_PNG :
$extension = 'png';
break;
case MemoryDrawing::MIMETYPE_GIF :
$extension = 'gif';
break;
case MemoryDrawing::MIMETYPE_JPEG :
$extension = 'jpg';
break;
}
$gdRes = $drawing->getImageResource();
ob_start();
if($extension == 'png'){
//设置透明
imagesavealpha($gdRes,true);
}
call_user_func(
$drawing->getRenderingFunction(),
$gdRes //$drawing->getImageResource()
);
$imageContents = ob_get_contents();
ob_end_clean();
} else {
$zipReader = fopen($drawing->getPath(), 'r');
$imageContents = '';
while (!feof($zipReader)) {
$imageContents .= fread($zipReader, 1024);
}
fclose($zipReader);
$extension = $drawing->getExtension();
}
//列从 0开始,行从1开始 和数据保持一致
list($imageStartColumn, $imageStartRow) = Coordinate::coordinateFromString($drawing->getCoordinates());
$imageStartColumn = Coordinate::columnIndexFromString($imageStartColumn) -1 ;
$folder_name = sys_get_temp_dir();
$myFileName = time() . '_' . mt_rand(100000, 999999) . '.' . $extension;
$images[] = $arr[$imageStartRow][$imageStartColumn] = $folder_name.'/'.$myFileName;
file_put_contents("{$folder_name}/{$myFileName}", $imageContents);
}
$excleDatas[$i] = $arr; // 多个sheet的数组的集合
}
return array('data' => $excleDatas, 'images' => $images);
}
注意的地方:
$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xls(); // setReadDataOnly Set read data only 只读单元格的数据,不格式化 e.g. 读时间会变成一个数据等
不要对 $read 进行 setReadDataOnly的设置为true 这样做会使下边无法获取到图片资源,也会对时间等内容产生影响;
难点的解决代码:
$gdRes = $drawing->getImageResource();
ob_start();
if($extension == 'png'){
//设置透明
imagesavealpha($gdRes,true);
}
解决的思路:
1. 查看PhpSpreadsheet 的文档查找对 png格式特殊处理的函数;结果没有找到!
2. 断点调试代码打印 $gdRes 对象;结果发现它就是 gd库的图片资源类型;这就说明可以使用原始的gd库的方法 imagesavealpha()来设置透明了