java+vue实现文件下载进度条

java+vue实现文件下载进度条

  • 方案
  • [1. Java 后端(Spring Boot)](#1. Java 后端(Spring Boot))
  • [2. Vue组件](#2. Vue组件)
  • [3. 效果](#3. 效果)
  • [4. 可继续扩展](#4. 可继续扩展)

方案

先让后端"流式写入 "并把已写入字节数放到内存里,前端发两次请求:

/download/{fileKey} 真正下文件;

/progress/{fileKey} 每 500 ms 轮询一次进度。


1. Java 后端(Spring Boot)

java 复制代码
@RestController
@RequestMapping("/api")
public class FileDownloadController {

    // key=文件唯一标识,value=已写入字节
    private final Map<String, AtomicLong> progressMap = new ConcurrentHashMap<>();

    @GetMapping("/download/{fileKey}")
    public void download(@PathVariable String fileKey,
                         HttpServletResponse resp) throws IOException {
        File file = new File("D:/tmp/bigfile.zip");   // 任意大文件
        long total = file.length();
        AtomicLong counter = new AtomicLong(0);
        progressMap.put(fileKey, counter);

        resp.setContentType("application/octet-stream");
        resp.setHeader("Content-Disposition",
                "attachment;filename=" + URLEncoder.encode(file.getName(), "UTF-8"));
        resp.setContentLengthLong(total);

        try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
             ServletOutputStream out = resp.getOutputStream()) {
            byte[] buf = new byte[8192];
            int len;
            while ((len = bis.read(buf)) != -1) {
                out.write(buf, 0, len);
                counter.addAndGet(len);   // 实时累加
            }
            out.flush();
        } finally {
            progressMap.remove(fileKey);
        }
    }

    @GetMapping("/progress/{fileKey}")
    public Map<String, Long> progress(@PathVariable String fileKey) {
        AtomicLong c = progressMap.get(fileKey);
        long done = c == null ? 0 : c.get();
        return Map.of("done", done);
    }
}

  • 通过bis.read(buf) 读取文件字节数
  • 返回值 len 表示本次实际读到的字节数,可能小于或等于 buf.length(这里是 8192 字节)。
  • 当文件读完时,read 返回 -1。

2. Vue组件

vue 复制代码
<template>
  <div>
    <el-button type="primary" @click="handleDownload">下载文件</el-button>
    <el-progress
      v-if="percent > 0"
      :percentage="percent"
      :stroke-width="12"
      style="width: 360px"
    />
  </div>
</template>

<script>
export default {
  data() {
    return {
      percent: 0,
      fileKey: ''   // 本次下载的唯一标识
    };
  },
  methods: {
    handleDownload() {
      this.fileKey = Date.now() + '';   // 简单生成 key
      this.percent = 0;

      // 1. 先启动轮询
      const timer = setInterval(async () => {
        const { data } = await this.$axios.get(`/api/progress/${this.fileKey}`);
        const done = data.done;
        // 假设总大小 200 MB(可按需让后端再接口返回 total)
        const total = 200 * 1024 * 1024;
        this.percent = Math.round((done / total) * 100);
        if (this.percent >= 100) clearInterval(timer);
      }, 500);

      // 2. 真正下载文件(不阻塞 UI)
      this.$axios({
        url: `/api/download/${this.fileKey}`,
        method: 'get',
        responseType: 'blob'
      }).then(res => {
        // 浏览器触发保存
        const blob = new Blob([res.data]);
        const link = document.createElement('a');
        link.href = URL.createObjectURL(blob);
        link.download = 'bigfile.zip';
        link.click();
        URL.revokeObjectURL(link.href);
      }).catch(() => {
        clearInterval(timer);
        this.percent = 0;
      });
    }
  }
};
</script>

3. 效果

  • 点击"下载文件"按钮 → 进度条从 0% 开始实时增长;
  • 下载完成自动触发浏览器保存窗口;
  • 支持任意大文件,不占内存(后端流式输出,前端 Blob 接收)。

4. 可继续扩展

  • /progress 接口同时返回 total,前端即可精确百分比;
  • 下载失败/取消时清除轮询;
  • 多文件同时下载时给每个文件分配独立 fileKey 即可。
相关推荐
小此方3 小时前
「C++AI大模型接入SDK」(一) API接入与本地两种方式对比、API Key获取、API报文详解与简单API的构建
开发语言·c++·人工智能
wuyk5554 小时前
Python实战项目02:学生成绩管理系统(控制台|CSV导出|完整落地)
开发语言·python
mldong9 小时前
一个 App,十三套后端:手机审批端 uni-jeeflow-app 开源了
java·架构
tqs_1234510 小时前
MySQL RR隔离级别死锁|Gap间隙锁、临键锁,订单并发范围查询死锁根因方案
java
逆境不可逃11 小时前
Pi Agent 学习笔记:多个工具怎样并行执行
java
浪子明X11 小时前
十六位账本怎样发现传输差错:Internet Checksum 生活类比
开发语言
尾善爱看海11 小时前
Vue 面试收官篇:SSR、性能优化落地、30 道高频面试题精讲(附标准答案)
前端·javascript·vue.js·面试·vue
weixin_BYSJ198711 小时前
【计算机毕设】基于SpringBoot与Vue的文物保护档案管理系统08621
vue.js·spring boot·spring cloud·微服务·架构·django·课程设计
Flynt11 小时前
Java 27 升级实测:默认值动得比新特性多,有个老参数会让 JVM 直接起不来
java·jvm·后端
Bs_MoneyMagnet11 小时前
基于springboot+vue的个人健康管理系统的设计与实现 源码+文档
java·vue.js·spring boot·后端·vue3·springboot3·计算机毕业设计