在前端开发中,文件下载是极其常见的需求。从简单的图片下载到大型文件处理,从静态资源到动态接口数据,不同场景需要不同的实现方案。
本文将全面总结前端文件下载的各种实现方式,包括基础方案、进阶方案和高级方案,并给出完整的代码示例,帮助你根据具体场景选择最适合的实现方式。
# 一、基础下载方案
# 1. a标签下载
最简单直接的下载方式,适合静态资源或可直接访问的URL。
// 基础下载 - 触发浏览器默认行为
const link = document.createElement('a')
link.href = 'https://example.com/file.pdf'
link.click()
// 带下载文件名 - 使用download属性
const link = document.createElement('a')
link.href = 'https://example.com/file.pdf'
link.download = '自定义文件名.pdf' // 告诉浏览器触发下载
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
// 注意事项:download属性有跨域限制
// 仅适用于同源URL或 blob:、data: URL
@前端进阶之旅: 代码已经复制到剪贴板
适用场景:同源静态文件下载、可控服务器资源
# 2. location.href 跳转
最简单的下载方式,浏览器会直接打开或下载目标资源。
// 直接跳转下载
location.href = 'https://example.com/file.pdf'
// 优点:简单直接
// 缺点:会跳转当前页面,无法获取下载进度
@前端进阶之旅: 代码已经复制到剪贴板
适用场景:不关心下载状态、一次性下载
# 3. window.open 打开
通过新窗口打开资源,适合浏览器直接支持预览的文件类型。
// 新窗口打开下载
window.open('https://example.com/file.pdf', '_blank')
// 适用于PDF、图片等浏览器可直接预览的文件
@前端进阶之旅: 代码已经复制到剪贴板
适用场景:需要浏览器预览的文件、需要在新标签页查看
# 二、Blob文件下载
# 4. fetch + Blob
处理接口返回的二进制数据,灵活控制下载行为。
// fetch下载文件(推荐方式)
async function downloadFile(url, filename) {
try {
const response = await fetch(url)
if (!response.ok) throw new Error('下载失败')
// 获取Blob对象
const blob = await response.blob()
// 创建下载链接
const blobUrl = URL.createObjectURL(blob)
// 创建a标签触发下载
const link = document.createElement('a')
link.href = blobUrl
link.download = filename
document.body.appendChild(link)
link.click()
// 清理
document.body.removeChild(link)
URL.revokeObjectURL(blobUrl) // 释放内存
} catch (error) {
console.error('下载失败:', error)
}
}
// 使用
downloadFile('https://example.com/api/download', 'file.pdf')
@前端进阶之旅: 代码已经复制到剪贴板
# 5. axios + Blob
类似fetch,适合已有axios项目的场景。
import axios from 'axios'
async function downloadWithAxios(url, filename) {
const response = await axios.get(url, {
responseType: 'blob', // 关键:指定响应类型为blob
})
const blob = new Blob([response.data])
const link = document.createElement('a')
link.href = URL.createObjectURL(blob)
link.download = filename
link.click()
URL.revokeObjectURL(link.href)
}
// 带请求头的下载
async function downloadWithToken(url, filename) {
const response = await axios.get(url, {
responseType: 'blob',
headers: {
Authorization: `Bearer ${token}`,
},
})
const blob = new Blob([response.data])
const link = document.createElement('a')
link.href = URL.createObjectURL(blob)
link.download = filename
link.click()
}
@前端进阶之旅: 代码已经复制到剪贴板
# 三、带进度下载
# 6. XMLHttpRequest 下载
可以获取下载进度,适合大文件或需要显示进度条的场景。
function downloadWithProgress(url, filename, onProgress) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
xhr.open('GET', url, true)
// 获取响应类型为blob
xhr.responseType = 'blob'
// 进度监听
xhr.onprogress = (event) => {
if (event.lengthComputable) {
const percent = Math.round((event.loaded / event.total) * 100)
onProgress && onProgress(percent)
}
}
xhr.onload = () => {
if (xhr.status === 200) {
const blob = xhr.response
const link = document.createElement('a')
link.href = URL.createObjectURL(blob)
link.download = filename
link.click()
resolve()
} else {
reject(new Error('下载失败'))
}
}
xhr.onerror = () => reject(new Error('网络错误'))
xhr.send()
})
}
// 使用 - 显示进度条
downloadWithProgress(
'https://example.com/big-file.zip',
'big-file.zip',
(percent) => {
console.log(`下载进度: ${percent}%`)
// 更新UI进度条
document.getElementById('progress').style.width = `${percent}%`
}
)
@前端进阶之旅: 代码已经复制到剪贴板
# 7. axios + onDownloadProgress
axios也支持进度监听。
import axios from 'axios'
async function downloadWithAxiosProgress(url, filename) {
const response = await axios.get(url, {
responseType: 'blob',
onDownloadProgress: (progressEvent) => {
const percent = Math.round(
(progressEvent.loaded * 100) / progressEvent.total
)
console.log(`下载进度: ${percent}%`)
},
})
const blob = new Blob([response.data])
const link = document.createElement('a')
link.href = URL.createObjectURL(blob)
link.download = filename
link.click()
}
@前端进阶之旅: 代码已经复制到剪贴板
# 四、特殊场景下载
# 8. Base64图片下载
下载页面中已存在的Base64图片或canvas生成的图片。
// Base64字符串下载
function downloadBase64(base64Data, filename, mimeType = 'image/png') {
const link = document.createElement('a')
link.href = base64Data
link.download = filename
link.click()
}
// Canvas图片下载
function downloadCanvasImage(canvasId, filename) {
const canvas = document.getElementById(canvasId)
// 转换为Base64
const dataUrl = canvas.toDataURL('image/png')
const link = document.createElement('a')
link.href = dataUrl
link.download = filename
link.click()
}
// 从图片元素下载
function downloadImageElement(imgElement, filename) {
const canvas = document.createElement('canvas')
canvas.width = imgElement.naturalWidth
canvas.height = imgElement.naturalHeight
const ctx = canvas.getContext('2d')
ctx.drawImage(imgElement, 0, 0)
canvas.toBlob((blob) => {
const link = document.createElement('a')
link.href = URL.createObjectURL(blob)
link.download = filename
link.click()
URL.revokeObjectURL(link.href)
})
}
@前端进阶之旅: 代码已经复制到剪贴板
# 9. 纯文本/JSON下载
下载纯文本内容或JSON数据。
# 10. 隐藏iframe下载
适合不想创建a标签或需要更隐蔽下载的场景。
# 五、高级下载方案
# 11. 大文件分片下载
处理超大文件,避免一次性加载导致内存溢出。
# 12. 流式下载(适合超大文件)
使用ReadableStream边下载边保存,适合真正的大型文件。