laravel-dompdf与WebSocket集成:实时推送PDF生成状态更新

laravel-dompdf与WebSocket集成:实时推送PDF生成状态更新

【免费下载链接】laravel-dompdf A DOMPDF Wrapper for Laravel 【免费下载链接】laravel-dompdf 项目地址: https://gitcode.com/gh_mirrors/la/laravel-dompdf

在Web应用开发中,当用户需要生成大型PDF文件时,长时间的等待往往会导致糟糕的用户体验。传统的同步生成方式会让用户在浏览器前一直等待,不清楚生成进度,甚至可能误以为系统已经崩溃。本文将介绍如何将laravel-dompdf与WebSocket技术集成,实现PDF生成状态的实时推送,让用户能够清晰地了解PDF生成的每一步进度,极大地提升用户体验。

项目概述

laravel-dompdf是一个为Laravel框架设计的DOMPDF封装器,它允许开发者轻松地将HTML内容转换为PDF文件。该项目的核心文件包括src/PDF.php,它提供了加载HTML内容、设置PDF选项、生成并输出PDF等功能。配置文件config/dompdf.php允许开发者自定义DOMPDF的各种选项,如字体目录、临时目录、默认纸张大小等。服务提供者src/ServiceProvider.php则负责将laravel-dompdf集成到Laravel框架中,进行依赖注入和配置加载。

集成WebSocket的必要性

在传统的PDF生成流程中,用户发起PDF生成请求后,服务器会同步处理该请求,直到PDF文件生成完成后才将结果返回给用户。这种方式存在以下问题:

  1. 用户体验差:用户需要长时间等待,且无法了解生成进度。
  2. 连接超时风险:对于大型PDF文件,生成时间可能较长,容易导致浏览器连接超时。
  3. 服务器资源占用:长时间的同步请求会占用服务器连接资源,影响服务器处理其他请求的能力。

通过集成WebSocket技术,我们可以将PDF生成过程改为异步处理,并通过WebSocket实时向客户端推送生成状态,如“开始生成”、“正在渲染页面”、“生成完成”等,从而解决上述问题。

实现方案

技术选型

  1. laravel-dompdf:用于PDF文件的生成,核心代码在src/PDF.php
  2. Laravel WebSocket:可以使用laravel-websockets包或pusher作为WebSocket服务。
  3. 队列系统:利用Laravel的队列系统处理PDF生成任务,避免阻塞主线程。

实现步骤

1. 安装必要依赖

首先,确保已经安装了laravel-dompdf。如果尚未安装,可以通过Composer进行安装:

composer require barryvdh/laravel-dompdf

然后,安装WebSocket相关依赖,以laravel-websockets为例:

composer require beyondcode/laravel-websockets
2. 配置WebSocket服务

发布laravel-websockets的配置文件:

php artisan vendor:publish --provider="BeyondCode\LaravelWebSockets\WebSocketsServiceProvider" --tag="config"

修改配置文件config/websockets.php,设置WebSocket服务的相关参数,如端口、主机等。

3. 创建PDF生成队列任务

创建一个队列任务,用于异步处理PDF生成:

php artisan make:job GeneratePdf

在生成的任务类中,注入PDF facade,并实现PDF生成逻辑。在生成过程中,通过事件触发状态更新,并通过WebSocket推送出去。

<?php

namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Barryvdh\DomPDF\Facade\Pdf;
use App\Events\PdfGenerationStatusUpdated;

class GeneratePdf implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    protected $html;
    protected $userId;

    public function __construct($html, $userId)
    {
        $this->html = $html;
        $this->userId = $userId;
    }

    public function handle()
    {
        // 触发开始生成事件
        event(new PdfGenerationStatusUpdated($this->userId, 'starting', 0));

        $pdf = Pdf::loadHTML($this->html);
        
        // 设置PDF选项,可参考[config/dompdf.php](https://link.gitcode.com/i/5d1c91d047681ba59c5a44a4aae3b714)中的配置
        $pdf->setPaper('a4', 'portrait');
        
        // 触发正在生成事件
        event(new PdfGenerationStatusUpdated($this->userId, 'generating', 50));

        $pdf->save(storage_path('app/public/pdf/document.pdf'));
        
        // 触发生成完成事件
        event(new PdfGenerationStatusUpdated($this->userId, 'completed', 100, storage_path('app/public/pdf/document.pdf')));
    }
}
4. 创建状态更新事件

创建一个事件类,用于表示PDF生成状态的更新:

php artisan make:event PdfGenerationStatusUpdated

事件类代码如下:

<?php

namespace App\Events;

use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class PdfGenerationStatusUpdated implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public $userId;
    public $status;
    public $progress;
    public $filePath;

    public function __construct($userId, $status, $progress, $filePath = null)
    {
        $this->userId = $userId;
        $this->status = $status;
        $this->progress = $progress;
        $this->filePath = $filePath;
    }

    public function broadcastOn()
    {
        return new PrivateChannel('pdf-generation.' . $this->userId);
    }

    public function broadcastAs()
    {
        return 'pdf-status-updated';
    }
}
5. 设置WebSocket广播

config/broadcasting.php中配置广播驱动为pusherlog(开发环境):

'default' => env('BROADCAST_DRIVER', 'pusher'),

如果使用laravel-websockets作为本地WebSocket服务,需要相应地配置pusher连接信息:

'pusher' => [
    'driver' => 'pusher',
    'key' => env('PUSHER_APP_KEY'),
    'secret' => env('PUSHER_APP_SECRET'),
    'app_id' => env('PUSHER_APP_ID'),
    'options' => [
        'cluster' => env('PUSHER_APP_CLUSTER'),
        'encrypted' => true,
        'host' => '127.0.0.1',
        'port' => 6001,
        'scheme' => 'http',
    ],
],
6. 前端实现WebSocket连接和状态显示

在前端页面中,使用JavaScript连接WebSocket服务,并监听PDF生成状态更新事件,实时显示生成进度。

import Echo from 'laravel-echo';

window.Echo = new Echo({
    broadcaster: 'pusher',
    key: 'your-pusher-key',
    wsHost: window.location.hostname,
    wsPort: 6001,
    forceTLS: false,
    disableStats: true,
});

window.Echo.private('pdf-generation.' + userId)
    .listen('.pdf-status-updated', (e) => {
        console.log('Status updated:', e.status);
        console.log('Progress:', e.progress);
        
        // 更新页面上的进度显示
        document.getElementById('progress-bar').style.width = e.progress + '%';
        document.getElementById('status-text').textContent = e.status;
        
        if (e.status === 'completed') {
            // 显示下载链接
            const downloadLink = document.getElementById('download-link');
            downloadLink.href = '/storage/pdf/document.pdf';
            downloadLink.style.display = 'block';
        }
    });
7. 触发PDF生成任务

在控制器中,当用户请求生成PDF时,将PDF生成任务分发到队列中:

<?php

namespace App\Http\Controllers;

use App\Jobs\GeneratePdf;
use Illuminate\Http\Request;

class PdfController extends Controller
{
    public function generate(Request $request)
    {
        $html = view('pdf.template', $request->all())->render();
        $userId = auth()->id();
        
        GeneratePdf::dispatch($html, $userId);
        
        return response()->json([
            'message' => 'PDF generation started. You will be notified when it is completed.'
        ]);
    }
}

关键代码解析

PDF生成核心逻辑

src/PDF.php中,loadHTML方法用于加载HTML内容,render方法用于渲染PDF,output方法用于获取PDF文件内容,save方法用于将PDF文件保存到磁盘。

// 加载HTML内容
public function loadHTML(string $string, ?string $encoding = null): self
{
    $string = $this->convertEntities($string);
    $this->dompdf->loadHtml($string, $encoding);
    $this->rendered = false;
    return $this;
}

// 渲染PDF
public function render(): void
{
    $this->dompdf->render();
    if ($this->showWarnings) {
        // 处理警告信息
    }
    $this->rendered = true;
}

// 保存PDF文件
public function save(string $filename, ?string $disk = null): self
{
    $disk = $disk ?: $this->config->get('dompdf.disk');
    if (! is_null($disk)) {
        Storage::disk($disk)->put($filename, $this->output());
        return $this;
    }
    $this->files->put($filename, $this->output());
    return $this;
}

WebSocket事件广播

在PDF生成任务中,通过触发PdfGenerationStatusUpdated事件,并通过WebSocket广播该事件,实现状态的实时推送。事件类中定义了广播的频道和事件名称,前端通过订阅相应的频道和监听事件来接收状态更新。

队列任务处理

通过Laravel的队列系统,将PDF生成任务异步处理,避免了同步请求导致的用户长时间等待和服务器资源占用问题。队列任务的处理逻辑在handle方法中实现,包括加载HTML内容、设置PDF选项、渲染PDF、保存文件等步骤,并在关键节点触发状态更新事件。

总结

通过将laravel-dompdf与WebSocket技术集成,我们实现了PDF生成状态的实时推送,极大地提升了用户体验。用户可以清晰地了解PDF生成的进度,不再需要长时间盲目等待。同时,通过使用Laravel的队列系统,实现了PDF生成任务的异步处理,提高了服务器的并发处理能力。

在实际应用中,还可以进一步扩展该方案,如添加PDF生成失败的状态推送、实现更详细的进度百分比计算、添加取消生成任务的功能等。通过不断优化,可以为用户提供更加完善和友好的PDF生成体验。

项目的官方文档可以参考README.md,其中包含了laravel-dompdf的详细使用说明和配置选项。如果需要深入了解PDF生成的具体实现,可以查看src/PDF.php中的代码。对于WebSocket集成部分,Laravel的官方文档和laravel-websockets的文档也提供了丰富的参考资料。

【免费下载链接】laravel-dompdf A DOMPDF Wrapper for Laravel 【免费下载链接】laravel-dompdf 项目地址: https://gitcode.com/gh_mirrors/la/laravel-dompdf

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值