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

Laravel 9与原生JS实现Storage文件下载的问题排查

问题:Laravel 9下载PDF文件时出现about:blank#blocked,修改后文件损坏无法打开

免责声明

提出此问题前,我已查阅相关技术帖子及Laravel 9官方文档。

上下文

  • Laravel 9全栈项目
  • 前端使用原生JS,无框架依赖
  • Storage目录结构:
    storage
    └── app
        └── public
            └── folder1
                ├── folder1A
                ├── folder1B
                ├── folder1C
                └── ...
    
  • 每个folder1X目录下仅存一个未知文件名的.pdf文件
  • 无空目录或损坏文件

问题描述

我通过FileController.php的方法下载folder1X/内的PDF,后端代码如下:

public function downloadFileFromStorage(Request $request): mixed
{
    $dirpath = $request->dirpath; // dirpath = public/folder1/folder1X

    $files = Storage::allFiles($dirpath);

    return response()->download(storage_path('app\\' . $files[0]));
}

注:dirpath由客户端通过axios请求发送,此前从数据库获取

前端点击按钮触发downloadPDF(dirpath)函数实现下载:

function downloadPDF(dirpath) {
    axios.post('/download-pdf-file', { dirpath })
        .then(
            success => {
                const url = success.data
                const a = document.createElement('a')
                a.download = 'file.pdf'
                a.href = url
                a.click()
            },
            error => {
                console.log(error.response)
            }
        )
}

执行后出现about:blank#blocked错误。

已尝试方案

  • 前端改用window.open(url)替代a标签方式;
  • 后端改为Storage::download($files[0], 'file-name.pdf'),前端结合Blob处理:
    success => {
        const blob = new Blob([success.data], { type: 'application/pdf' })
        const fileURL = URL.createObjectURL(blob)
        window.openURL(fileURL)
    },
    
  • 尝试Blob与a标签结合的方式;
  • 修改storage_path参数,拼接$files[0]前添加/app/public/。

更新

根据建议修改代码后可下载文件,但PDF损坏无法打开:

修改后的JS代码

// FileDownload已通过require()导入
function downloadPDF(dirpath) {
    axios({
        url: '/download-pdf-file',
        method: 'GET',
        responseType: 'blob',
        options: {
            body: { dirpath }
        }
    }).then(
        success => {
            FileDownload(success.data, 'nota-fiscal.pdf')
        }
    )
}

修改后的PHP代码

public function downloadFileFromStorage(Request $request): mixed
{
    $dirpath = $request->dirpath; // dirpath = public/folder1/folder1X

    $files = Storage::allFiles($dirpath);
    return Storage::download($files[0], 'filename.pdf');
}

解决方案

问题根源

  1. GET请求中用options.body传递参数无效,后端无法获取正确dirpath,返回内容不是合法PDF数据;
  2. axios处理Blob时的编码或参数传递错误,导致文件内容损坏。

修复步骤

1. 后端调整参数接收逻辑

如果使用GET请求,参数需放在URL的query中,修改路由和控制器:

// routes/web.php
Route::get('/download-pdf-file', [FileController::class, 'downloadFileFromStorage']);

// FileController.php
use Illuminate\Support\Str;

public function downloadFileFromStorage(Request $request): mixed
{
    $dirpath = $request->query('dirpath'); // 从query获取参数
    $files = Storage::allFiles($dirpath);
    
    // 校验文件有效性
    if(empty($files) || !Str::endsWith($files[0], '.pdf')) {
        abort(404, '文件不存在或格式错误');
    }
    
    return Storage::download($files[0], 'nota-fiscal.pdf');
}

2. 前端正确传递参数并处理Blob

修改JS代码,将dirpath作为query参数传递,并用原生方式处理Blob下载:

function downloadPDF(dirpath) {
    axios({
        url: '/download-pdf-file',
        method: 'GET',
        responseType: 'blob',
        params: { dirpath } // 用params传递GET参数
    }).then(success => {
        // 创建临时a标签触发下载
        const url = window.URL.createObjectURL(success.data);
        const a = document.createElement('a');
        a.href = url;
        a.download = 'nota-fiscal.pdf';
        document.body.appendChild(a);
        a.click();
        // 清理资源
        window.URL.revokeObjectURL(url);
        document.body.removeChild(a);
    }).catch(error => {
        console.error('下载失败:', error);
    });
}

3. 可选:改用POST请求的正确写法

如果坚持用POST请求,调整前端参数传递方式:

function downloadPDF(dirpath) {
    axios({
        url: '/download-pdf-file',
        method: 'POST',
        responseType: 'blob',
        data: { dirpath } // 用data传递POST参数
    }).then(success => {
        const url = window.URL.createObjectURL(success.data);
        const a = document.createElement('a');
        a.href = url;
        a.download = 'nota-fiscal.pdf';
        document.body.appendChild(a);
        a.click();
        window.URL.revokeObjectURL(url);
        document.body.removeChild(a);
    }).catch(error => {
        console.error('下载失败:', error);
    });
}

后端保持$request->dirpath获取参数即可。

关键注意点

  • 确保Storage使用public磁盘(Laravel默认配置),否则需在方法中指定磁盘:Storage::disk('public')->allFiles($dirpath);
  • 必须校验文件存在性和格式,避免返回无效内容;
  • 下载完成后释放Blob URL资源,防止内存泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 21:50:15