Vue中如何停止报表重复下载?解决轮询与缓存问题
问题分析与修复方案
核心问题
- 后端用同一个接口同时处理状态查询和文件下载,导致文件未就绪时返回无效下载响应,前端无法正确判断状态。
- 前端通过
axios.get请求下载接口,会将二进制文件解析为文本,无法通过response.data !== ''判断文件是否就绪。 - 浏览器缓存GET请求,导致轮询时无法获取最新状态,直接读取磁盘缓存。
- 每次调用接口都会重新分发异步Job,可能产生重复任务。
后端修复
1. 拆分接口:状态查询与文件下载分离
创建两个独立接口,分别负责触发Job/查询状态和文件下载。
Controller 修改
public function checkReportStatus() { $user = request()->user(); $cacheKey = 'FullReport-' . $user->id; $processingKey = 'FullReport-Processing-' . $user->id; // 避免重复分发Job,仅在无缓存且未处理时触发 if (!Cache::has($cacheKey) && !Cache::has($processingKey)) { Cache::put($processingKey, true, now()->addMinutes(5)); ExcelReport::dispatch('FullReport', $user); } // 返回状态信息 if (Cache::has($cacheKey)) { return response()->json(['status' => 'ready', 'path' => Cache::get($cacheKey)]); } else { return response()->json(['status' => 'processing']); } } public function downloadReport() { $user = request()->user(); $cacheKey = 'FullReport-' . $user->id; $processingKey = 'FullReport-Processing-' . $user->id; $path = Cache::get($cacheKey); if (!$path) { return response()->json(['error' => '文件未就绪'], 404); } // 下载完成后清理缓存标记 Cache::forget($cacheKey); Cache::forget($processingKey); return response()->download($path); }
Job 修改(添加处理完成标记清除)
public function handle() { $cacheKey = $this->reportName . '-' . $this->user->id; $processingKey = $this->reportName . '-Processing-' . $this->user->id; $report = excelReportService()->getFullReport($this->reportName, $this->user); Cache::put($cacheKey, $report); // 清除处理中标记 Cache::forget($processingKey); }
前端修复
1. 调整轮询逻辑,请求状态接口而非下载接口
2. 添加缓存破防,避免浏览器缓存轮询请求
3. 文件就绪时通过新窗口触发下载
Vue 组件修改
<template> <div class="card" style="width: 100%;"> <div class="card-body"> <ul> <li class="nav-item"> <a class="nav-link text-success downloadReport" @click="startDownloadProcess"> <i v-if="!showSpinner" class="fas fa-file-excel"></i> <i v-if="showSpinner" class="fa-solid fa-spinner fa-spin-pulse"></i> Report </a> </li> </ul> </div> </div> </template> <script> export default { data() { return { pollingInterval: null, showSpinner: false } }, methods: { startDownloadProcess() { if (this.pollingInterval) return; // 防止重复触发 this.showSpinner = true; this.pollingInterval = setInterval(this.checkReportStatus, 2000); }, async checkReportStatus() { try { // 添加时间戳参数破防缓存 const response = await axios.get("/report/status", { params: { t: Date.now() } }); if (response.data.status === 'ready') { clearInterval(this.pollingInterval); this.pollingInterval = null; // 新窗口触发下载,避免缓存和阻塞问题 window.open("/report/download", '_blank'); this.showSpinner = false; } } catch (error) { console.error('状态查询失败:', error); clearInterval(this.pollingInterval); this.pollingInterval = null; this.showSpinner = false; } }, } } </script>
额外注意事项
- 确保缓存的文件路径是服务器可访问的绝对路径,避免
response()->download报错。 - 根据业务需求调整缓存过期时间,避免存储过多无用文件。
- 生产环境需添加权限验证,确保用户只能下载自己的报告。
内容的提问来源于stack exchange,提问作者Starla
相关产品推荐
相关产品推荐

