从业务需求到可下载文件 (Blob + a 标签方式下载)

以下以excel文件为例

一、业务场景

假设页面上有一张列表,每条数据后面都有一个“导出”按钮。用户点击后,希望导出当前数据对应的文件。

这个需求本质上是一个“文件下载”场景,但它与普通的 JSON 接口不同:

  • 返回值是文件流,而不是业务数据对象。
  • 需要处理响应头中的文件名。
  • 需要兼容不同浏览器对下载行为的差异。
  • 需要在失败时给用户一个明确反馈,而不是静默失败。

二、核心实现思路

前端导出文件的标准做法,可以概括成一句话:

发起 GET 请求拿到二进制流,读取响应头中的文件名,构造 Blob 下载链接并触发浏览器下载。

下面是一个适合测试环境的 mock 写法:

async excelExport(id) {
  // 先锁定页面,避免用户在导出过程中重复点击
  const loading = this.$loading({
    lock: true,
    text: "正在导出中...",
    spinner: "el-icon-loading",
  });

  try {
    // 这里使用虚假地址(需要自行替换成真实接口)
    const url = `https://test.hi.com/mock-api/document/export?id=${id}`;

    // 直接请求文件流,注意这里返回的不是普通 JSON
    const response = await fetch(url, {
      method: "GET",
      credentials: "include",
    });

    // 先判断响应状态,避免把错误页面也当成文件下载
    if (!response.ok) {
      throw new Error("导出失败,接口返回异常");
    }

    // 优先从响应头中解析文件名,保持服务端命名一致
    const disposition = response.headers.get("content-disposition") || "";
    const fileName = (() => {
      const utf8Match = disposition.match(/filename\*=UTF-8''([^;]+)/i);
      if (utf8Match?.[1]) return decodeURIComponent(utf8Match[1]);

      const fileMatch = disposition.match(/filename="?([^"]+)"?/i);
      if (fileMatch?.[1]) return fileMatch[1];

      return `导出文件_${Date.now()}.xlsx`;
    })();

    // 将二进制内容转成 Blob,再生成临时下载链接
    const blob = await response.blob();
    const blobUrl = window.URL.createObjectURL(blob);
    const link = document.createElement("a");
    link.href = blobUrl;
    link.style.display = "none";
    link.download = fileName;
    document.body.appendChild(link);

    // 模拟用户点击,触发浏览器下载行为
    link.click();
    document.body.removeChild(link);

    // 下载完成后及时释放临时 URL,避免内存浪费
    window.URL.revokeObjectURL(blobUrl);
  } catch (error) {
    console.error("导出失败:", error);
  } finally {
    // 无论成功还是失败,都要恢复页面状态
    loading.close();
  }
}

如果没有获取到文件名,需要后端同学配合修改接口相应头

Access-Control-Expose-Headers: Content-Disposition
Content-Disposition: attachment; filename=“report.xlsx”; filename*=UTF-8’'导出报表名称.xlsx

三、为什么要这样写

Blob + a 标签 是最通用的下载方案

这套方案兼容性比较好,适合大多数现代浏览器。核心逻辑是:

  • 把响应内容转成 Blob
  • URL.createObjectURL 生成临时地址
  • 创建一个隐藏的 <a> 标签
  • 通过 click() 触发下载
  • 下载完成后及时释放临时 URL
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值