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'); }
解决方案
问题根源
- GET请求中用
options.body传递参数无效,后端无法获取正确dirpath,返回内容不是合法PDF数据; - 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
相关产品推荐
相关产品推荐

