You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React+Laravel实现PDF下载时文件损坏的问题排查

问题场景

使用React前端+Laravel后端架构,尝试下载存储在public/uploads目录下的PDF文件,前后端代码如下,但下载的文件损坏且字节数远小于原文件。

前端代码

const downloadFile = async (attachmentName) => {
    try {
        const response = await fetch(`/attachments/${attachmentName}`);
        
        // Check if the response is OK
        if (!response.ok) {
            throw new Error(`Failed to download file: ${response.status} ${response.statusText}`);
        }
        
        // Create a blob URL for the file content
        const url = window.URL.createObjectURL(await response.blob());
        
        // Create a temporary link element to trigger the download
        const link = document.createElement('a');
        link.href = url;
        link.setAttribute('download', attachmentName);
        
        // Append the link to the document body and trigger the download
        document.body.appendChild(link);
        link.click();
        
        // Clean up
        document.body.removeChild(link);
        window.URL.revokeObjectURL(url);
    } catch (error) {
        console.error('Error downloading attachment:', error);
    }
}
<a href="#" onClick={() => downloadFile(job.attachments)}>
    <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#b72424" class="bi bi-file-earmark-pdf-fill" viewBox="0 0 16 16">
        <path d="M5.523 12.424q.21-.124.459-.238a8 8 0 0 1-.45.606c-.28.337-.498.516-.635.572l-.035.012a.3.3 0 0 1-.026-.044c-.056-.11-.054-.216.04-.36.106-.165.319-.354.647-.548m2.455-1.647q-.178.037-.356.078a21 21 0 0 0 .5-1.05 12 12 0 0 0 .51.858q-.326.048-.654.114m2.525.939a4 4 0 0 1-.435-.41q.344.007.612.054c.317.057.466.147.518.209a.1.1 0 0 1 .026.064.44.44 0 0 1-.06.2.3.3 0 0 1-.094.124.1.1 0 0 1-.069.015c-.09-.003-.258-.066-.498-.256M8.278 6.97c-.04.244-.108.524-.2.829a5 5 0 0 1-.089-.346c-.076-.353-.087-.63-.046-.822.038-.177.11-.248.196-.283a.5.5 0 0 1 .145-.04c.013.03.028.092.032.198q.008.183-.038.465z"/>
        <path fill-rule="evenodd" d="M4 0h5.293A1 1 0 0 1 10 .293L13.707 4a1 1 0 0 1 .293.707V14a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2m5.5 1.5v2a1 1 0 0 0 1 1h2zM4.165 13.668c.09.18.23.343.438.419.207.075.412.04.58-.03.318-.13.635-.436.926-.786.333-.401.683-.927 1.021-1.51a11.7 11.7 0 0 1 1.997-.406c.3.383.61.713.91.95.28.22.603.403.934.417a.86.86 0 0 0 .51-.138c.155-.101.27-.247.354-.416.09-.181.145-.37.138-.563a.84.84 0 0 0-.2-.518c-.226-.27-.596-.4-.96-.465a5.8 5.8 0 0 0-1.335-.05 11 11 0 0 1-.98-1.686c.25-.66.437-1.284.52-1.794.036-.218.055-.426.048-.614a1.24 1.24 0 0 0-.127-.538.7.7 0 0 0-.477-.365c-.202-.043-.41 0-.601.077-.377.15-.576.47-.651.823-.073.34-.04.736.046 1.136.088.406.238.848.43 1.295a20 20 0 0 1-1.062 2.227 7.7 7.7 0 0 0-1.482.645c-.37.22-.699.48-.897.787-.21.326-.275.714-.08 1.103"/>
    </svg>
    {getAttachmentName(job.attachments)}
</a>

后端代码

路由

Route::get('/attachments/{attachment}', [AttachmentController::class, 'show'])->name('attachments.show');

控制器

public function show($attachment)
{
    // Check if the file exists
    if (!Storage::disk('public')->exists($attachment)) {
        abort(404); // Or return a suitable error response
    }

    // Retrieve the file contents
    $fileContents = Storage::disk('public')->get($attachment);

    // Return the file as a response with the appropriate headers
    return response($fileContents, 200)
        ->header('Content-Type', 'application/pdf')
        ->header('Content-Disposition', 'attachment; filename="' . basename($attachment) . '"');
}

问题

文件可触发下载,但下载后损坏,字节数远小于原文件。


原因分析及解决方案

1. 存储路径不匹配

文件存放在项目根目录的public/uploads,但Laravel的Storage::disk('public')默认指向storage/app/public目录,而非项目根的public文件夹。如果未执行php artisan storage:link创建软链接,或传入的attachmentName未包含uploads/前缀,会导致读取错误路径的文件,返回非预期内容。

解决方案:

  • 若文件确实在public/uploads,改用public_path()直接定位文件:
public function show($attachment)
{
    $filePath = public_path("uploads/{$attachment}");
    if (!file_exists($filePath)) {
        abort(404);
    }
    return response()->download($filePath, basename($filePath), [
        'Content-Type' => 'application/pdf',
    ]);
}
  • 若要使用Storage,将文件移至storage/app/public/uploads,执行php artisan storage:link创建软链接,同时修改控制器路径:
public function show($attachment)
{
    $filePath = "uploads/{$attachment}";
    if (!Storage::disk('public')->exists($filePath)) {
        abort(404);
    }
    return Storage::disk('public')->download($filePath);
}

2. 二进制文件读取方式错误

Storage::get()会将文件内容当作字符串读取,PDF这类二进制文件易出现编码转换或截断问题,导致文件损坏。

解决方案:
使用Laravel内置的response()->download()或Storage::download()方法,它们会自动处理二进制文件的读取和响应头设置,避免手动读取内容的错误。

3. 前端响应处理优化

可添加Accept头明确告知后端需要二进制文件,避免潜在的内容协商问题:

const response = await fetch(`/attachments/${attachmentName}`, {
    headers: {
        'Accept': 'application/pdf'
    }
});

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 14:26:01