Laravel Livewire集成FPDI导出PDF报错解决方案咨询
问题原因分析
出现「SyntaxError: Unexpected token '%', "%PDF-1.5"」错误的核心原因是:Livewire的wire:click触发的是AJAX请求,而FPDF的Output('I', ...)会直接输出PDF二进制内容。AJAX会默认尝试将响应内容解析为JSON格式,而PDF开头的%PDF-1.5不符合JSON语法,因此抛出解析错误。
解决方案
要解决这个问题,需要避开AJAX处理文件下载的场景,改用普通HTTP请求(表单提交/路由跳转)结合Laravel控制器来处理PDF导出:
1. 修改Livewire组件的导出按钮
将原来的wire:click按钮替换为表单提交,直接请求专门的导出路由:
<form action="{{ route('direct-payment.export') }}" method="POST"> @csrf <!-- 传递导出所需的关键参数,比如付款记录ID --> <input type="hidden" name="direct_payment_id" value="{{ $data['direct_payment']->id }}"> <button type="submit" class="btn btn-warning text-white me-2"> <i class="bi bi-download"></i> <span class='btn-text'>Export</span> </button> </form>
2. 添加导出路由
在routes/web.php中新增POST路由,指向控制器的导出方法:
use App\Http\Controllers\DirectPaymentController; Route::post('/direct-payment/export', [DirectPaymentController::class, 'export']) ->name('direct-payment.export');
3. 迁移导出逻辑到控制器
将原来Livewire组件中的静态export方法迁移到控制器,并调整PDF输出方式,用Laravel的响应系统返回正确的PDF内容:
namespace App\Http\Controllers; use Illuminate\Http\Request; use setasign\Fpdi\Fpdi; use App\Models\DirectPayment; use App\Http\Controllers\Bir2307DateController; class DirectPaymentController extends Controller { public function export(Request $request) { // 获取导出所需数据 $directPayment = DirectPayment::findOrFail($request->direct_payment_id); // 根据你的业务逻辑获取公司信息,示例为从认证用户关联获取 $company = auth()->user()->company; $data = [ 'direct_payment' => $directPayment, 'company' => $company ]; // 原有的PDF生成逻辑 $period_month = date('n', strtotime($data['direct_payment']->date)); $get_year = date('Y', strtotime($data['direct_payment']->date)); $previos_quarter_month = Bir2307DateController::CheckBirPreviousMonth($period_month); $period_from = date($previos_quarter_month.'01'.$get_year); $last_quarter_month = app(Bir2307DateController::class)->CheckBirLastQuartMonth($period_month); $last_day = cal_days_in_month(CAL_GREGORIAN, $last_quarter_month, $get_year); $period_to = date($last_quarter_month.$last_day.$get_year); $period_from_split = str_split($period_from); $period_to_split = str_split($period_to); $vendor_tin = str_split($data['direct_payment']->vendorCustomer->tin); $vendor_zip_code = str_split($data['direct_payment']->vendorCustomer->address_zip_code); $vendor_address = $data['direct_payment']->vendorCustomer->address_street.", ".$data['direct_payment']->vendorCustomer->address_city.", ".$data['direct_payment']->vendorCustomer->address_province; $company_tin = str_split($data['company']->tin); $company_zip_code = ($data['company']->zip_code) ? str_split($data['company']->zip_code) : str_split("0000"); $company_address = $data['company']->street." ".$data['company']->brgy.", ".$data['company']->city.", ".$data['company']->province; $pdf = new Fpdi(); $page_count = $pdf->setSourceFile(public_path("/bir-form/bir-2307.pdf")); for ($pageNo = 1; $pageNo <= $page_count; $pageNo++) { $tplIdx = $pdf->importPage($pageNo); $pdf->AddPage(); $size = $pdf->getTemplateSize($tplIdx); $width = $size["width"]; $height = $size["height"]; $pdf->useTemplate($tplIdx, 5, 5, 205,280); if($pageNo == 1) { $pdf->SetFont('Times'); $pdf->SetTextColor(0, 0, 0); $pdf->SetFontSize(10); $pdf->Text(57,41, $period_from_split[0]); $pdf->Text(61,41, $period_from_split[1]); $pdf->Text(66,41, $period_from_split[2]); $pdf->Text(70,41, $period_from_split[3]); $pdf->Text(74.5,41, $period_from_split[4]); $pdf->Text(79,41, $period_from_split[5]); $pdf->Text(83.5,41, $period_from_split[6]); $pdf->Text(87.5,41, $period_from_split[7]); } } // 用Laravel响应系统输出PDF,确保头信息正确 return response()->stream(function() use ($pdf) { echo $pdf->Output('S'); // 'S'参数表示返回PDF字符串内容,而非直接输出 }, 200, [ 'Content-Type' => 'application/pdf', 'Content-Disposition' => 'inline; filename="direct_payment.pdf"' ]); } }
关键说明
- 放弃Livewire的AJAX请求:AJAX不适合处理文件下载场景,普通HTTP请求能让浏览器正确识别并处理PDF响应。
- 控制器处理响应:通过Laravel的
response()->stream()方法输出PDF,能精准设置Content-Type等头信息,避免FPDF直接输出导致的头冲突。 - 参数传递:通过表单隐藏字段传递导出所需的关键ID,而非直接传递复杂数据结构,更安全高效。
内容的提问来源于stack exchange,提问作者Angel
相关产品推荐
相关产品推荐

