前端文件下载全攻略:6 种方式详解与适用场景对比

83 阅读4分钟

在前端开发中,「文件下载」几乎是每个项目都会遇到的功能。
有的需要导出 Excel / PDF 报表,有的要生成海报图,还有的需要带 Token 请求后端文件流。

本文系统整理 6 种前端文件下载方式,带你一次性理清它们的区别、原理和适用场景。


一、前端下载方式总览

方式原理适用场景优缺点
<a download>原生属性触发浏览器下载静态文件、本地资源✅ 简单;❌ 不支持跨域、自定义名有限
window.open()打开链接交给浏览器处理后端已设置下载头✅ 简单;❌ 无法监听结果
canvas + a 标签Canvas绘制图片转Base64图片生成/截图✅ 可处理图片;❌ 不能跨域
fetch + blob文件流转Blob创建URL任意类型文件下载✅ 通用、安全;❌ 需现代浏览器
⑤ 后端 Content-Disposition服务端控制下载行为大文件、导出报表✅ 稳定安全;❌ 需后端支持
⑥ 第三方库 FileSaver.js封装Blob保存逻辑跨浏览器下载文件✅ 简化开发;❌ 需依赖库

二、各方式详解

1️、<a download> 直接下载

最简单的方式,无需 JS。

<a href="/files/demo.pdf" download="myFile.pdf">点击下载</a>

优点:

  • 简洁、零代码。
  • 可指定文件名(部分浏览器支持)。

缺点:

  • 不支持跨域资源。
  • 无法添加 Header 或 Token。
  • 仅适用于静态文件。

适用场景:

同源的静态资源、前端打包文件、简单下载页。


2️、window.open() 打开下载链接

window.open('https://example.com/report.pdf')

优点:

  • 最直接的方式,浏览器自动处理下载。
  • 不用创建标签、兼容性好。

缺点:

  • 无法控制文件名;
  • 有可能被浏览器拦截;
  • 无法判断下载是否成功。

适用场景:

后端接口已设置 Content-Disposition 头部。


3️、canvas + a 标签(图片下载方案)

function DownLoad(href, name = 'image') {
  const img = new Image()
  img.crossOrigin = 'anonymous'
  img.onload = function () {
    const canvas = document.createElement('canvas')
    canvas.width = this.width
    canvas.height = this.height
    canvas.getContext('2d').drawImage(this, 0, 0)
    const url = canvas.toDataURL('image/png')

    const a = document.createElement('a')
    a.href = url
    a.download = `${name}.png`
    a.click()
  }
  img.src = href + '?time=' + new Date().getTime()
}

优点:

  • 可用于截图、生成分享图、导出图表。
  • 不依赖后端。

缺点:

  • 仅支持图片类型;
  • 跨域图片会污染 Canvas,导致下载失败
  • 画质可能略有损失。

适用场景:

图片另存为、前端生成海报、截图导出。


4️、fetch + blob(通用文件流下载)

const handleDownload = async (url, filename) => {
  const response = await fetch(url, { mode: 'cors' })
  const blob = await response.blob()
  const blobUrl = URL.createObjectURL(blob)

  const a = document.createElement('a')
  a.href = blobUrl
  a.download = filename || url.split('/').pop()
  a.click()
  URL.revokeObjectURL(blobUrl)
}

优点:

  • 支持任意文件类型(PDF、Excel、ZIP等)。
  • 可携带 Token、Cookie;可鉴权。
  • 下载的文件原样保存(无压缩)。

缺点:

  • 需现代浏览器支持;
  • 不支持断点续传。

适用场景:

绝大多数文件下载需求:接口返回文件流、带 Token 请求等。


5️、后端 Content-Disposition 控制下载

后端设置响应头:

Content-Type: application/octet-stream
Content-Disposition: attachment; filename="report.xlsx"

前端直接:

window.location.href = '/api/download/report?id=123'

优点:

  • 浏览器原生下载;
  • 不占用前端内存;
  • 适合大文件(可断点续传)。

缺点:

  • 需要后端支持;
  • 无法自定义文件名(由后端控制)。

适用场景:

后端生成并导出大文件(如报表、日志包、导出 ZIP)。


6️、使用第三方库 FileSaver.js

import { saveAs } from 'file-saver'

fetch('/api/export')
  .then(res => res.blob())
  .then(blob => saveAs(blob, 'report.xlsx'))

优点:

  • 封装完善,兼容 IE10+;
  • 简化 fetch + blob 操作。

缺点:

  • 需额外依赖库;
  • 原理与 Blob 相同。

适用场景:

大型项目、需要兼容旧浏览器时。


三、跨域下载问题说明

方案跨域支持说明
<a download>No仅限同源文件
window.open()Yes浏览器直接请求
canvas + aNo需服务端设置 Access-Control-Allow-Origin
fetch + blobYes可通过 mode: 'cors' 处理
后端下载Yes由服务端控制
FileSaver.jsYes基于 blob

总结:如果你无法修改后端响应头,请优先选择 fetch + blob 或后端导出方案。


四、选型建议

需求类型推荐方案
下载同源静态文件<a download>
导出图片或海报canvas + a 标签
接口返回文件流(带 Token)fetch + blob
大文件下载(报表、压缩包)后端 Content-Disposition
需要兼容性和易用性FileSaver.js

五、总结

“下载的本质,就是让浏览器获得一个可访问的文件流。”

  • 图片场景 → canvas + a
  • 普通文件 → fetch + blob
  • 大文件/服务端导出 → 后端响应头
  • 快速方案 → <a download> / FileSaver.js

写在最后

开发中选方案,关键在于理解下载的“发起方”和“控制方”是谁。 前端能直接拿到数据的,就自己触发; 后端生成的文件,就交给服务器响应。