You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Laravel中Maatwebsite/Excel导出Excel转PDF方案咨询(保格式图片)

解决Laravel Maatwebsite/Excel导出Excel转PDF时保留图片、格式及公式的方案

我之前做Laravel项目时也遇到过一模一样的问题——直接改扩展名把Excel转成PDF,结果图片没了、格式乱成一锅粥,连公式都失效了。其实这是因为Excel和PDF的底层渲染逻辑完全不同,单纯改扩展名根本不是正经的转换方式。针对你的需求,这里有几个经过实践验证的方案,都能保留图片、原有格式和特殊公式:

方案一:利用PhpSpreadsheet原生PDF导出(和Maatwebsite/Excel无缝衔接)

Maatwebsite/Excel底层依赖PhpSpreadsheet,而PhpSpreadsheet本身支持直接导出PDF,我们可以借助它来实现需求,还能完美衔接现有代码。

步骤:

  1. 先安装PDF渲染引擎,比如Dompdf或者TCPDF(二选一即可):
# 安装Dompdf
composer require dompdf/dompdf

# 或者安装TCPDF
composer require tecnickcom/tcpdf
  1. 修改你的OrderExport类,添加事件监听来处理PDF导出:
use Maatwebsite\Excel\Concerns\FromQuery;
use Maatwebsite\Excel\Concerns\Exportable;
use Maatwebsite\Excel\Concerns\WithEvents;
use Maatwebsite\Excel\Events\AfterSheet;
use PhpOffice\PhpSpreadsheet\Writer\Pdf\Dompdf; // 用TCPDF就替换为对应类
use Illuminate\Http\Response;

class OrderExport implements FromQuery, WithEvents
{
    use Exportable;

    protected $order;

    public function __construct(Order $order)
    {
        $this->order = $order;
    }

    public function query()
    {
        // 保留你原有的查询逻辑,比如:
        return Order::where('id', $this->order->id);
    }

    public function registerEvents(): array
    {
        return [
            AfterSheet::class => function(AfterSheet $event) {
                // 获取PhpSpreadsheet核心实例
                $spreadsheet = $event->sheet->getDelegate()->getParent();
                
                // 初始化PDF Writer(以Dompdf为例)
                $writer = new Dompdf($spreadsheet);
                // 设置纸张尺寸和方向
                $writer->setPaper('A4', 'portrait');
                // 开启公式预计算,确保PDF显示公式结果
                $writer->setPreCalculateFormulas(true);
                
                // 生成PDF内容
                $pdfContent = $writer->generate();
                
                // 返回下载响应
                return response($pdfContent)
                    ->header('Content-Type', 'application/pdf')
                    ->header('Content-Disposition', "attachment; filename=\"{$this->order->no}.pdf\"");
            },
        ];
    }
}
  1. 控制器的export方法可保持不变,或直接调用上述PDF生成逻辑即可。

这个方案的优势是和现有代码无缝集成,无需额外系统依赖,但Dompdf对复杂Excel格式的支持有限,样式复杂的话可以试试TCPDF。

方案二:用LibreOffice命令行转换(兼容性最强)

如果你的Excel包含非常复杂的格式、图片和特殊公式,LibreOffice的命令行转换是最靠谱的选择——它对Excel的兼容性几乎完美,能1:1还原所有内容。

步骤:

  1. 在服务器上安装LibreOffice:

    • Ubuntu/Debian:sudo apt-get install libreoffice libreoffice-writer libreoffice-calc
    • CentOS/RHEL:sudo yum install libreoffice-headless libreoffice-writer libreoffice-calc
  2. 修改控制器的export方法,先导出临时Excel,再调用命令行转PDF:

use Illuminate\Support\Facades\Storage;
use Illuminate\Http\Response;

public function export(Order $order)
{
    // 定义临时文件路径
    $excelFileName = "{$order->no}.xlsx";
    $pdfFileName = "{$order->no}.pdf";
    $tempExcelPath = storage_path("app/temp/{$excelFileName}");
    $tempPdfPath = storage_path("app/temp/{$pdfFileName}");

    // 先导出Excel到临时目录
    Excel::store(new OrderExport($order), "temp/{$excelFileName}");

    // 调用LibreOffice无头模式转换PDF
    $command = "libreoffice --headless --convert-to pdf \"{$tempExcelPath}\" --outdir " . storage_path('app/temp');
    exec($command);

    // 读取PDF内容并返回下载
    $pdfContent = file_get_contents($tempPdfPath);

    // 清理临时文件(避免占用服务器空间)
    Storage::delete("temp/{$excelFileName}");
    Storage::delete("temp/{$pdfFileName}");

    return response($pdfContent)
        ->header('Content-Type', 'application/pdf')
        ->header('Content-Disposition', "attachment; filename=\"{$pdfFileName}\"");
}

注意:确保storage/app/temp目录存在且有读写权限,Windows服务器需调整命令的LibreOffice路径。

这个方案唯一的缺点是需要服务器安装LibreOffice,但兼容性是所有方案里最好的,适合复杂场景。

方案三:Snappy HTML转PDF(适合简单表格场景)

如果你的Excel结构比较简单,可以先把Excel导出成HTML,再用Laravel Snappy包转成PDF。不过这个方案需要提前计算好公式,且HTML渲染的格式可能和Excel有细微差异,适合简单场景。

步骤:

  1. 安装Snappy包:
composer require barryvdh/laravel-snappy
  1. 修改控制器的export方法:
use Barryvdh\Snappy\Facades\Pdf;

public function export(Order $order)
{
    // 先把Excel导出成HTML内容
    $htmlContent = Excel::raw(new OrderExport($order), \Maatwebsite\Excel\Excel::HTML);
    
    // 用Snappy生成PDF
    $pdf = Pdf::loadHTML($htmlContent);
    
    // 返回下载
    return $pdf->download("{$order->no}.pdf");
}

这个方案配置简单,但对复杂格式和公式的支持不如前两个方案。


总结一下:如果服务器能安装LibreOffice,优先选方案二;不能安装的话用方案一;简单表格可以用方案三。

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

火山引擎 最新活动