You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel Excel导出PNG图片失败:仅显路径、仅导出单张图

问题核心原因
  • 初版导出仅写入图片路径字符串:Laravel Excel默认不会自动将单元格内的路径字符串解析为嵌入图片,因此只会显示文本路径。
  • 实现WithDrawings接口后仅导出1张图,本质是三个实现错误:
    1. drawings()方法仅实例化了单个Drawing对象,没有为每一行数据生成独立的Drawing实例,底层PhpSpreadsheet会以最后一次赋值的坐标覆盖之前的配置,最终只显示1张图
    2. 没有按行号动态计算每个图片对应的单元格坐标,所有图片要么叠在第一行位置,要么因坐标冲突被覆盖
    3. 图片路径使用了相对路径/网络路径,除第一张路径命中的图片外,其余图片因文件找不到被自动跳过
代码遗漏点清单
  • 未在遍历数据时维护行号与图片的映射关系,图片坐标和数据行错位
  • 复用单个Drawing实例给多行赋值,导致坐标配置互相覆盖
  • 图片列未留空,写入的路径字符串和插入的图片重叠
  • 未给数据行设置适配图片高度的行高,图片被截断
  • 未做图片存在性校验,单张图片丢失就会中断整个导出流程
修复实现方案

确保已安装maatwebsite/excel:^3.1版本,直接替换导出类代码即可:

<?php

namespace App\Exports;

use App\Models\Signature;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithDrawings;
use Maatwebsite\Excel\Concerns\WithStyles;
use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PhpOffice\PhpSpreadsheet\Style\Alignment;

class SignatureExport implements FromCollection, WithHeadings, WithDrawings, WithStyles
{
    /**
     * 待插入的所有图片实例集合
     * @var Drawing[]
     */
    protected array $drawings = [];

    /**
     * 签名图片所在列
     * @var string
     */
    protected string $imageColumn = 'D';

    /**
     * 表头占1行,数据从第2行开始
     * @var int
     */
    protected int $dataStartRow = 2;

    public function collection()
    {
        // 查全量签名数据
        $signatures = Signature::all();

        // 遍历数据逐行生成对应图片实例
        foreach ($signatures as $index => $signature) {
            $currentRow = $this->dataStartRow + $index;
            // 拼接图片绝对物理路径,不要用url/asset生成的网络地址
            $imageAbsolutePath = storage_path('app/public/' . ltrim($signature->sign_path, '/'));

            // 图片不存在直接跳过,避免导出中断
            if (!file_exists($imageAbsolutePath)) {
                continue;
            }

            // 必须为每行new全新的Drawing实例,禁止复用对象
            $drawing = new Drawing();
            $drawing->setName(sprintf('签名_%s', $signature->id));
            $drawing->setPath($imageAbsolutePath);
            // 绑定当前行对应单元格坐标
            $drawing->setCoordinates($this->imageColumn . $currentRow);
            // 设置边距偏移,避免图片贴单元格边框
            $drawing->setOffsetX(5);
            $drawing->setOffsetY(5);
            // 统一图片尺寸,等比缩放
            $drawing->setHeight(60);
            $drawing->setResizeProportional(true);

            $this->drawings[] = $drawing;
        }

        // 返回导出数据,图片列返回空字符串即可
        return $signatures->map(function ($signature) {
            return [
                'id' => $signature->id,
                'signer_name' => $signature->signer_name,
                'sign_time' => $signature->sign_time?->format('Y-m-d H:i:s'),
                'sign_image' => '', // 图片列留空,由drawings接口插入图片
                'remark' => $signature->remark
            ];
        });
    }

    public function headings(): array
    {
        return [
            '记录ID',
            '签署人姓名',
            '签署时间',
            '手写签名',
            '备注'
        ];
    }

    public function drawings(): array
    {
        return $this->drawings;
    }

    public function styles(Worksheet $sheet)
    {
        $totalRows = Signature::count() + $this->dataStartRow;
        // 逐行设置行高,适配图片高度
        for ($row = $this->dataStartRow; $row <= $totalRows; $row++) {
            $sheet->getRowDimension($row)->setRowHeight(70);
        }
        // 图片列设置垂直居中
        $sheet->getStyle(sprintf('%s:%s', $this->imageColumn, $this->imageColumn))
            ->getAlignment()
            ->setVertical(Alignment::VERTICAL_CENTER);
    }
}

控制器导出方法保持原有逻辑即可,不需要额外加响应头:

<?php

namespace App\Http\Controllers;

use App\Exports\SignatureExport;
use Maatwebsite\Excel\Facades\Excel;

class ExportController extends Controller
{
    public function exportSignature()
    {
        $fileName = sprintf('签名记录导出_%s.xlsx', date('YmdHis'));
        return Excel::download(new SignatureExport(), $fileName);
    }
}
避坑提醒
  1. 不要在drawings()方法内查询数据生成图片实例:该方法在导出流程中会被多次调用,在collection遍历阶段生成图片实例可以避免重复查库、行号错位问题
  2. 绝对禁止复用Drawing实例:PhpSpreadsheet底层会以对象最后一次set的坐标为准,复用对象会导致所有图片叠在同一个单元格
  3. 图片路径必须用本地绝对路径:Drawing类不支持直接读取远程网络图片,远程图片需要先下载到本地临时目录再插入
  4. 不要在图片列单元格写入内容:插入的图片会悬浮在单元格上方,写入的路径/文字会显示在图片下方

内容的提问来源于stack exchange,提问作者JUSTIN CHUA

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 06:45:31