Laravel Excel导出签名图片失败 仅显示路径不显示实际图片
问题根源
你当前代码有3个核心错误,导致签名图片无法正常导出:
- 导出类没有实现
WithDrawings接口,就算写了drawings()方法,Laravel Excel也不会识别执行 drawings()方法里写的是签名存储的文件夹路径,不是单张签名图片的完整路径,而且坐标写死在B3单元格,没法批量匹配每一行的签名列- 默认查询返回的signature字段是数据库里存的路径字符串,没有做清空处理,导出后会直接显示路径文本
导出功能修复方案
按以下步骤调整代码即可实现预期效果:
- 引入并实现
WithDrawings、WithEvents接口,前者负责识别图片导出逻辑,后者用来批量设置行高,避免图片显示不全 - 提前在构造方法中查询所有签名数据,避免重复查库,遍历数据时把signature字段置空,防止导出路径字符串
- 遍历所有签名记录,给每行D列(签名列)生成对应的图片对象,拼接单张图片的完整路径,做好文件存在校验避免报错
- 批量设置数据行的行高,匹配图片尺寸,同时补上缺失的列宽配置
修正后的Export类代码如下:
<?php namespace App\Exports; use App\Models\signature; use Maatwebsite\Excel\Concerns\FromCollection; use PhpOffice\PhpSpreadsheet\Worksheet\Drawing; use Maatwebsite\Excel\Concerns\WithHeadings; use Maatwebsite\Excel\Concerns\WithColumnWidths; // 新增引入图片导出、事件相关接口 use Maatwebsite\Excel\Concerns\WithDrawings; use Maatwebsite\Excel\Concerns\WithEvents; use Maatwebsite\Excel\Events\AfterSheet; class SignatureExport implements FromCollection, WithHeadings, WithColumnWidths, WithDrawings, WithEvents { /** * 存储签名数据集,供其他方法复用避免重复查询 */ protected $signatures; public function __construct() { $this->signatures = signature::all(); } /** * @return \Illuminate\Support\Collection */ public function collection() { // 清空signature字段值,避免导出路径字符串 return $this->signatures->map(function($item) { $item->signature = ''; return $item; }); } /** * 批量生成所有行的签名图片对象 */ public function drawings() { $drawings = []; // 表头占第1行,业务数据从第2行开始 foreach ($this->signatures as $index => $sign) { $row = $index + 2; $imagePath = public_path('/uploads/signatures/' . $sign->signature); // 跳过不存在的图片,避免导出报错 if (!file_exists($imagePath)) continue; $drawing = new Drawing(); $drawing->setName('signature'); $drawing->setDescription('User signature'); $drawing->setPath($imagePath); $drawing->setHeight(90); // 定位到当前行的D列(签名列) $drawing->setCoordinates('D' . $row); // 设置边距避免图片贴单元格边框 $drawing->setOffsetX(5); $drawing->setOffsetY(5); $drawings[] = $drawing; } return $drawings; } public function headings(): array { return [ 'id', 'name', 'description', 'signature', 'created_at', 'updated_at', ]; } public function columnWidths(): array { return [ 'A' => 30, 'B' => 30, 'C' => 30, 'D' => 30, 'E' => 30, 'F' => 30, // 补全updated_at对应列的宽度配置 ]; } /** * 批量设置行高,保证图片完整显示 */ public function registerEvents(): array { return [ AfterSheet::class => function(AfterSheet $event) { $totalRows = count($this->signatures) + 1; // 从第2行开始统一设置行高适配图片尺寸 for ($i = 2; $i <= $totalRows; $i++) { $event->sheet->getRowDimension($i)->setRowHeight(80); } }, ]; } }
原有控制器代码不需要调整,如果需要导出.xls格式,把download方法里的文件名后缀改成.xls即可。
对应场景的Excel导入实现
带签名图片的Excel导入核心逻辑是提取表格内嵌图片保存到本地,再关联到对应行数据,实现步骤如下:
- 生成导入类,实现
ToModel、WithHeadingRow、WithEvents接口 - 在
AfterSheet事件中遍历工作表内所有图片,筛选出签名列的图片保存到本地签名目录,记录图片和行号的映射关系 - 写入模型数据时,根据当前行号取出对应图片的存储文件名,存入数据库
基础Import类代码示例:
<?php namespace App\Imports; use App\Models\signature; use Maatwebsite\Excel\Concerns\ToModel; use Maatwebsite\Excel\Concerns\WithHeadingRow; use Maatwebsite\Excel\Concerns\WithEvents; use Maatwebsite\Excel\Events\AfterSheet; use Illuminate\Support\Str; class SignatureImport implements ToModel, WithHeadingRow, WithEvents { // 存储行号和对应签名图片文件名的映射 protected $rowSignMap = []; public function registerEvents(): array { return [ AfterSheet::class => function(AfterSheet $event) { $sheet = $event->sheet->getDelegate(); // 遍历所有嵌入图片 foreach ($sheet->getDrawingCollection() as $drawing) { $coordinate = $drawing->getCoordinates(); $column = substr($coordinate, 0, 1); // 只处理D列(签名列)的图片 if ($column != 'D') continue; $row = substr($coordinate, 1); // 生成唯一文件名存储 $extension = $drawing->getExtension() ?: 'png'; $filename = Str::random(20) . '.' . $extension; $savePath = public_path('uploads/signatures/' . $filename); file_put_contents($savePath, file_get_contents($drawing->getPath())); $this->rowSignMap[$row] = $filename; } } ]; } public function model(array $row) { // 静态变量记录当前处理行号,表头占第1行,数据从第2行开始 static $currentRow = 2; $signFile = $this->rowSignMap[$currentRow] ?? ''; $currentRow++; return new signature([ 'name' => $row['name'], 'description' => $row['description'], 'signature' => $signFile, ]); } }
控制器导入方法参考:
public function importexcel(Request $request) { $request->validate([ 'file' => 'required|file|mimes:xls,xlsx' ]); Excel::import(new SignatureImport, $request->file('file')); return back()->with('success', '导入成功'); }
内容的提问来源于stack exchange,提问作者JUSTIN CHUA
相关产品推荐
相关产品推荐

