前后端文件下载完整实战:从响应头到 Blob

前后端文件下载完整实战:从响应头到 Blob

在开发时,一个绕不开的功能就是:

「用户一键下载自己生成的项目代码」

表面上是一个"下载按钮",但背后涉及到:

权限校验

后端流式输出 ZIP

HTTP 响应头设计

前端 fetch + Blob 下载

这篇文章我结合真实项目代码,完整拆解一套前后端协作的文件下载方案。

一、整体方案概览

我们先看整体流程(非常关键):

前端:

点击「下载代码」

使用 fetch 发起 GET 请求(携带 Cookie)

后端返回 二进制流(zip)

前端将响应转成 Blob

使用 URL.createObjectURL 触发浏览器下载

后端:

校验应用是否存在

校验当前用户是否是创建者

定位生成好的代码目录

将目录打包为 ZIP

通过 HttpServletResponse 以流的形式返回

二、前端实现:fetch + Blob 的标准下载姿势

1️⃣ 下载状态与按钮控制

ts

复制代码

const downloading = ref(false)

2️⃣ 使用 fetch 发起下载请求

ts

复制代码

const downloadCode = async () => {

if (!appId.value) {

message.error('应用ID不存在')

return

}

downloading.value = true

try {

const API_BASE_URL = request.defaults.baseURL || ''

const url = `${API_BASE_URL}/app/download/${appId.value}`

const response = await fetch(url, {

method: 'GET',

credentials: 'include', // ⭐ 非常关键

})

if (!response.ok) {

throw new Error(`下载失败: ${response.status}`)

}

credentials: 'include'

用于携带 Cookie(登录态)

否则后端 getLoginUser 会直接失败

不用 axios?

axios 对二进制下载反而更麻烦

fetch + blob 是浏览器原生、最稳定方案

3️⃣ 从响应头中解析文件名

ts

复制代码

const contentDisposition = response.headers.get('Content-Disposition')

const fileName =

contentDisposition?.match(/filename="(.+)"/)?.[1]

|| `app-${appId.value}.zip`

为什么要从响应头拿文件名?

后端可以动态控制下载名称

避免前端硬编码

符合 HTTP 标准

4️⃣ Blob 是什么?为什么一定要用它?

Blob(Binary Large Object)本质上是:浏览器中的"二进制文件对象"

ZIP / PDF / 图片 / Excel

在 JS 世界里都必须先变成 Blob 才能下载

5️⃣ 触发浏览器下载(核心技巧)

ts

复制代码

const downloadUrl = URL.createObjectURL(blob)

const link = document.createElement('a')

link.href = downloadUrl

link.download = fileName

link.click()

URL.revokeObjectURL(downloadUrl)

这是目前浏览器最标准、兼容性最好的下载方式

6️⃣ 下载按钮示例

html

复制代码

type="primary"

ghost

@click="downloadCode"

:loading="downloading"

:disabled="!isOwner"

>

下载代码

三、后端实现:权限校验 + ZIP 流式下载

1️⃣ 接口定义

java

复制代码

@GetMapping("/download/{id}")

public void downloadAppCode(@PathVariable Long id,

HttpServletRequest request,

HttpServletResponse response)

2️⃣ 权限与合法性校验(必须)

3️⃣ 获取目录

4️⃣ 核心:如何设置响应头?

在 downloadProjectAsZip 内部,一定要做这几件事:

java

复制代码

response.setContentType("application/octet-stream");

response.setCharacterEncoding("UTF-8");

response.setHeader(

"Content-Disposition",

"attachment; filename=\"" + fileName + ".zip\""

);

如果不设置 Content-Disposition:

浏览器可能直接尝试"打开"

前端拿不到文件名

5️⃣ ZIP 流式输出(关键思想)

核心原则:

不要先生成 zip 文件再读, 边压缩,边写入 response 输出流

优点:

节省磁盘

支持大项目

更快响应

相关推荐

动感单车笔记
365体育比分官网

动感单车笔记

📅 09-06 👁️ 3696
【超星学习通助手】更新AI解答+视频+测试+考试(更新了AI辅助功能,题库调用失败时,可以用于手动查题)
5寸屏手机有哪些
365体育比分官网

5寸屏手机有哪些

📅 07-25 👁️ 6213