Laravel中Maatwebsite/Excel导出Excel转PDF方案咨询(保格式图片)
我之前做Laravel项目时也遇到过一模一样的问题——直接改扩展名把Excel转成PDF,结果图片没了、格式乱成一锅粥,连公式都失效了。其实这是因为Excel和PDF的底层渲染逻辑完全不同,单纯改扩展名根本不是正经的转换方式。针对你的需求,这里有几个经过实践验证的方案,都能保留图片、原有格式和特殊公式:
方案一:利用PhpSpreadsheet原生PDF导出(和Maatwebsite/Excel无缝衔接)
Maatwebsite/Excel底层依赖PhpSpreadsheet,而PhpSpreadsheet本身支持直接导出PDF,我们可以借助它来实现需求,还能完美衔接现有代码。
步骤:
- 先安装PDF渲染引擎,比如Dompdf或者TCPDF(二选一即可):
# 安装Dompdf composer require dompdf/dompdf # 或者安装TCPDF composer require tecnickcom/tcpdf
- 修改你的
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\""); }, ]; } }
- 控制器的
export方法可保持不变,或直接调用上述PDF生成逻辑即可。
这个方案的优势是和现有代码无缝集成,无需额外系统依赖,但Dompdf对复杂Excel格式的支持有限,样式复杂的话可以试试TCPDF。
方案二:用LibreOffice命令行转换(兼容性最强)
如果你的Excel包含非常复杂的格式、图片和特殊公式,LibreOffice的命令行转换是最靠谱的选择——它对Excel的兼容性几乎完美,能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
- Ubuntu/Debian:
修改控制器的
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有细微差异,适合简单场景。
步骤:
- 安装Snappy包:
composer require barryvdh/laravel-snappy
- 修改控制器的
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




