在 Laravel 中利用 DomPDF 将图片转换为 PDF 的完整指南
在 Laravel 项目开发中,经常会遇到需要将图片批量转换为 PDF 文件的需求,比如生成电子凭证、图片归档文档等场景。DomPDF 是 Laravel 生态中常用的 PDF 生成工具,能够基于 HTML 渲染生成 PDF,我们可以借助它的特性,通过构造包含图片的 HTML 页面来实现图片转 PDF 的功能。本文将详细介绍从环境配置到功能实现的完整流程。
一、环境准备与依赖安装
首先确保你已经有一个正常运行的 Laravel 项目,本文示例基于 Laravel 10 版本,其他版本操作逻辑基本一致。接下来需要安装 DomPDF 的 Laravel 适配包,打开终端进入项目根目录,执行以下命令:
composer require barryvdh/laravel-dompdf
安装完成后,需要发布 DomPDF 的配置文件,方便我们后续自定义配置,执行如下命令:
php artisan vendor:publish --provider="Barryvdh\DomPDF\ServiceProvider"
执行完成后,项目 config 目录下会生成 dompdf.php 配置文件,你可以根据需求调整 PDF 默认纸张大小、方向等参数,本文使用默认配置即可。
二、基础功能实现:单张图片转 PDF
我们先实现最基础的功能:将单张图片转换为 PDF 文件。首先创建一个控制器,用于处理图片转 PDF 的逻辑:
<?php
namespace App\Http\Controllers;
use Barryvdh\DomPDF\Facade\Pdf;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
class ImageToPdfController extends Controller
{
/**
* 单张图片转 PDF 方法
* @param Request $request
* @return \Illuminate\Http\Response
*/
public function singleImageToPdf(Request $request)
{
// 验证上传的图片文件
$request->validate([
'image' => 'required|image|mimes:jpeg,png,jpg,gif|max:20480',
]);
// 获取上传的图片文件
$imageFile = $request->file('image');
// 生成唯一文件名,避免重复
$imageName = uniqid() . '.' . $imageFile->getClientOriginalExtension();
// 将图片存储到 public 磁盘的 images 目录
$imagePath = $imageFile->storeAs('images', $imageName, 'public');
// 获取图片的完整访问路径
$fullImagePath = Storage::disk('public')->path($imagePath);
// 也可以获取 URL 形式路径,根据 DomPDF 配置选择,这里使用绝对路径更稳妥
// $imageUrl = Storage::disk('public')->url($imagePath);
// 构造包含图片的 HTML 内容
$html = '<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>图片转PDF</title>
<style>
body { margin: 0; padding: 0; }
.image-container { width: 100%; text-align: center; }
img { max-width: 100%; height: auto; }
</style>
</head>
<body>
<div class="image-container">
<img src="' . $fullImagePath . '" alt="待转换图片">
</div>
</body>
</html>';
// 加载 HTML 生成 PDF
$pdf = Pdf::loadHTML($html);
// 设置 PDF 纸张为 A4,方向为纵向
$pdf->setPaper('A4', 'portrait');
// 返回 PDF 下载响应,文件名可自定义
return $pdf->download('single_image_' . time() . '.pdf');
}
}上面的代码中,我们做了几个关键操作:首先验证上传的图片文件合法性,然后将图片存储到服务器指定目录,接着构造包含该图片的 HTML 页面,最后通过 DomPDF 加载 HTML 生成 PDF 并返回下载。注意这里使用了图片的绝对路径,能够避免 DomPDF 加载图片失败的问题。
接下来需要添加对应的路由,打开 routes/web.php 文件,添加如下路由:
use App\Http\Controllers\ImageToPdfController;
Route::post('/image-to-pdf/single', [ImageToPdfController::class, 'singleImageToPdf'])->name('image.to.pdf.single');三、进阶功能:多张图片合并为单个 PDF
实际场景中我们往往需要将多张图片合并到同一个 PDF 文件中,比如多页的图片文档需要归档。我们只需要在之前的控制器中新增一个方法即可实现:
<?php
// 省略之前的命名空间、use 引入和 singleImageToPdf 方法
/**
* 多张图片合并为单个 PDF 方法
* @param Request $request
* @return \Illuminate\Http\Response
*/
public function multiImageToPdf(Request $request)
{
// 验证上传的多张图片
$request->validate([
'images' => 'required|array|min:1',
'images.*' => 'image|mimes:jpeg,png,jpg,gif|max:20480',
]);
$imagePaths = [];
// 遍历上传的所有图片,存储并收集路径
foreach ($request->file('images') as $imageFile) {
$imageName = uniqid() . '.' . $imageFile->getClientOriginalExtension();
$imagePath = $imageFile->storeAs('images', $imageName, 'public');
$fullImagePath = Storage::disk('public')->path($imagePath);
$imagePaths[] = $fullImagePath;
}
// 构造包含多张图片的 HTML 内容,每张图片单独占一页
$html = '<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>多图转PDF</title>
<style>
body { margin: 0; padding: 0; }
.page { page-break-after: always; width: 100%; text-align: center; }
.page:last-child { page-break-after: auto; }
.page img { max-width: 100%; height: auto; }
</style>
</head>
<body>';
foreach ($imagePaths as $path) {
$html .= '<div class="page">
<img src="' . $path . '" alt="图片">
</div>';
}
$html .= '</body></html>';
// 生成 PDF
$pdf = Pdf::loadHTML($html);
$pdf->setPaper('A4', 'portrait');
// 返回下载响应
return $pdf->download('multi_image_' . time() . '.pdf');
}这个方法的核心逻辑是:首先验证上传的多图数组,然后将所有图片存储并收集路径,构造 HTML 时给每个图片容器添加 page-break-after: always 样式,让每张图片在 PDF 中单独占一页,最后合并生成同一个 PDF 文件。
对应的路由配置如下:
Route::post('/image-to-pdf/multi', [ImageToPdfController::class, 'multiImageToPdf'])->name('image.to.pdf.multi');四、常见问题与解决方法
- 图片无法加载到 PDF 中:首先检查图片路径是否正确,建议使用服务器绝对路径而非 URL 路径;如果是 Linux 系统,还需要确保运行 PHP 的用户对图片文件有读取权限。
- PDF 中图片变形:可以在 CSS 中给图片设置
max-width: 100%; height: auto;样式,让图片自适应 PDF 页面宽度,保持原始比例。 - PDF 生成速度慢:如果图片数量多或者体积大,可以适当压缩图片后再进行转换,也可以调整 DomPDF 配置中的渲染超时时间,避免请求超时。
五、简单前端上传示例
为了测试我们的功能,可以编写一个简单的前端上传页面,放在 resources/views 目录下,命名为 image_upload.blade.php:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>图片转PDF</title>
<style>
.container { max-width: 800px; margin: 50px auto; padding: 20px; border: 1px solid #eee; }
.form-group { margin-bottom: 20px; }
label { display: block; margin-bottom: 8px; font-weight: bold; }
input[type="file"] { margin-bottom: 10px; }
button { padding: 10px 20px; background: #007bff; color: #fff; border: none; border-radius: 4px; cursor: pointer; }
button:hover { background: #0056b3; }
.tip { color: #666; font-size: 14px; margin-top: 10px; }
</style>
</head>
<body>
<div class="container">
<h2>单张图片转PDF</h2>
<form action="{{ route('image.to.pdf.single') }}" method="POST" enctype="multipart/form-data">
@csrf
<div class="form-group">
<label>选择图片:</label>
<input type="file" name="image" accept="image/*" required>
<div class="tip">支持 jpeg、png、jpg、gif 格式,单张最大 20MB</div>
</div>
<button type="submit">生成PDF</button>
</form>
<hr>
<h2>多张图片合并为PDF</h2>
<form action="{{ route('image.to.pdf.multi') }}" method="POST" enctype="multipart/form-data">
@csrf
<div class="form-group">
<label>选择多张图片:</label>
<input type="file" name="images[]" accept="image/*" multiple required>
<div class="tip">支持多选,每张图片单独占一页,其余限制同单张上传</div>
</div>
<button type="submit">生成合并PDF</button>
</form>
</div>
</body>
</html>然后在控制器中添加一个方法渲染这个页面,路由指向该方法即可访问测试页面,上传图片后就能下载对应的 PDF 文件。
通过以上步骤,我们就完成了在 Laravel 中使用 DomPDF 实现图片转 PDF 的完整功能,你可以根据项目的实际需求,进一步扩展功能,比如给 PDF 添加页眉页脚、调整图片布局、设置 PDF 密码等。