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

Vue中如何停止报表重复下载?解决轮询与缓存问题

问题分析与修复方案

核心问题

  1. 后端用同一个接口同时处理状态查询和文件下载,导致文件未就绪时返回无效下载响应,前端无法正确判断状态。
  2. 前端通过axios.get请求下载接口,会将二进制文件解析为文本,无法通过response.data !== ''判断文件是否就绪。
  3. 浏览器缓存GET请求,导致轮询时无法获取最新状态,直接读取磁盘缓存。
  4. 每次调用接口都会重新分发异步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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 20:13:14