情景:一条数据中某字段包含多张图片,分单元格存放导出
类库:maatwebsite(Laravel基于PHPExcel进行封装)
使用:
下载:
composer require "maatwebsite/excel:~2.1.0"
注册:
在config/app.php的 providers 中添加
Maatwebsite\Excel\ExcelServiceProvider::class,
注册到门面:
'Excel' => Maatwebsite\Excel\Facades\Excel::class,
发布配置设置:
php artisan vendor:publish --provider="Maatwebsite\Excel\ExcelServiceProvider"
其他:
(•ˇ‸ˇ•)没找到支持导出网络图片的方法,只好先用curl缓存到本地,然后防止硬盘爆炸,写了个定时任务删掉缓存的图片。
将网络图片保存到本地:
function getCloudImg($url, $path){
if (!file_exists($path)) {
mkdir($path, 0777, true);
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
$file = curl_exec($ch);
curl_close($ch);
$filename = pathinfo($url, PATHINFO_BASENAME);
$resource = fopen($path . '\\' . $filename, 'w+');
fwrite($resource, $file);
fclose($resource);
return $path .'\\'. $filename;
}
计算图片存放坐标:
function getExcelMapOffset($count, $start = 'A')
{
//定义excelMap
$excelMap = [
'A',
'B',
'C',
...
'Z'
];
//判断起始偏移列名在excelMap键名+偏移后长度是否大于excelMap长度
$mapCount = count($excelMap);
$startKey = array_search($start, $excelMap);
//不大于直接返回偏移列名
if ($startKey + $count <= $mapCount - 1) {
return $excelMap[$startKey + $count];
} else {
//大于excelMap长度,取商在excelMap作为键名查找第一位,取余数在excelMap中查找第二位
return $excelMap[($startKey + $count) / $mapCount - 1] . $excelMap[($startKey + $count) % $mapCount];
}
}
Excel导出部分:
use Maatwebsite\Excel\Facades\Excel;
//$celldata为已构造好结构的文字数据,$cloudImgs为按照key与$celldata数据对应的网络图片地址
Excel::create($fileName, function ($excel) use ($celldata, $cloudImgs) {
$excel->sheet('score', function ($sheet) use ($celldata, $cloudImgs) {
$sheet->rows($celldata);
//设置图片本地路径,加上时间戳用于定时任务删除判断
$path = config('app.excelImageCachePath') . '\\' . time();
foreach ($cloudImgs as $key => $item) {
foreach ($item as $k => $img) {
//计算列名
$x = Example::getExcelMapOffset('S', $k);
//用网络地址换取本地存储路径
$cloudImg = UploadHelper::getCloudImg($img, $path);
$objDrawing = new \PHPExcel_Worksheet_Drawing();
$objDrawing->setPath($cloudImg);
//设置图片坐标(单元格)
$objDrawing->setCoordinates($x . ($key + 2));
//限定图片高度
$objDrawing->setHeight(100);
//图片在单元格中的偏移位置(若不设置则图位于单元格左上方)
$objDrawing->setOffsetX(10);
$objDrawing->setOffsetY(10);
$objDrawing->setRotation(100);
$objDrawing->setWorksheet($sheet);
//如果有图片,把列拉宽
$sheet->setWidth([$x => 40]);
}
}
//设置单元格样式(第一行为title)
$setCount = count($celldata[0]);
for ($c = 2; $c < $setCount; $c++) {
$sheet->setHeight([//设置第二行起每一行的高度
$c => 100,
]);
}
//设置每一行宽度
$sheet->setWidth([
'A' => 15,
'B' => 15,
....
]);
//计算总数据量
$count = count($celldata);
$sheet->cells("A1:R$count", function ($cells) {
//设置单元格内元素靠左向上
$cells->setAlignment('left');
$cells->setValignment('top');
});
//标题加粗
$sheet->cells("A1:S1", function ($cells) {
$cells->setFontWeight('bold');
});
});
})->export('xls');
更新:
今天学到了一个新东西:在处理字符变量的算数运算时,PHP 沿袭了 Perl 的习惯,而非 C 的。
$a = 'Z';
$a++;
将把 $a 变成 ‘AA’,而在 C 中,
a = 'Z';
a++;
将把 a 变成 ‘[‘(’Z’ 的 ASCII 值是 90,’[’ 的 ASCII 值是 91)。
注意字符变量只能递增,不能递减,并且只支持纯字母(a-z 和 A-Z)。
所以之前的单元格坐标计算可以换成:
function getExcelMapOffset($count, $start = 'A')
{
$start = 'A';
for($i=0;$i<$count;$i++){
$start++;
}
}
return $start;