一、主要下载方法
1. a标签下载法
最传统、最简单的前端下载方式。
代码示例
// 方法1:直接设置href
function downloadByAnchor(url, filename) {
const a = document.createElement('a');
a.href = url;
a.download = filename || 'download';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
// 方法2:使用Blob URL
function downloadByBlob(data, filename, mimeType) {
const blob = new Blob([data], { type: mimeType });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
// 清理
setTimeout(() => {
document.body.removeChild(a);
URL.revokeObjectURL(url);
}, 100);
}
-
优点:
简单易用,兼容性好(包括旧版浏览器)
支持大文件下载(通过URL)
可以自定义下载文件名
支持跨域资源(服务器需正确配置CORS)
-
缺点:
对于动态生成的内容需要创建Blob和Object URL
无法直接下载非同源文件(需服务器CORS支持)
某些浏览器可能忽略download属性
-
使用建议:
适合已知URL的静态资源下载
适合中小型文件(<50MB)
当需要支持旧版浏览器时优先考虑
2. window.open方法
通过打开新窗口或标签页实现下载。
代码示例
function downloadByWindowOpen(url) {
// 方法1:直接打开
window.open(url, '_blank');
// 方法2:结合iframe(避免弹出拦截)
const iframe = document.createElement('iframe');
iframe.style.display = 'none';
iframe.src = url;
document.body.appendChild(iframe);
setTimeout(() => {
document.body.removeChild(iframe);
}, 1000);
}
-
优点:
实现简单,一行代码即可
兼容所有浏览器
支持各种文件类型
-
缺点:
容易被浏览器弹出拦截
无法自定义下载文件名
用户体验较差(会打开新标签页)
无法处理动态生成的内容
-
使用建议:
仅作为备选方案
适用于内部系统,可以禁用弹出拦截
适合简单的文件下载需求
3. location.href方法
通过重定向当前页面实现下载。
代码示例:
function downloadByLocation(url) {
// 方法1:直接重定向
window.location.href = url;
// 方法2:创建form表单(避免页面跳转)
function downloadByForm(url, params) {
const form = document.createElement('form');
form.method = 'GET';
form.action = url;
form.target = '_blank';
// 添加参数
Object.keys(params).forEach(key => {
const input = document.createElement('input');
input.type = 'hidden';
input.name = key;
input.value = params[key];
form.appendChild(input);
});
document.body.appendChild(form);
form.submit();
document.body.removeChild(form);
}
}
-
优点:
极其简单
兼容性好
支持GET请求下载
-
缺点:
会离开当前页面(除非使用target="_blank")
无法POST请求下载
不能自定义文件名
无法处理动态内容
-
使用建议:
适用于简单的GET下载
适合全页面下载场景
不推荐在SPA应用中使用
4. Fetch/XMLHttpRequest + Blob
最灵活强大的下载方式,适用于复杂场景。
代码示例
// 使用Fetch API
async function downloadByFetch(url, filename, options = {}) {
try {
const response = await fetch(url, options);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const blob = await response.blob();
const downloadUrl = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = downloadUrl;
a.download = filename || getFilenameFromResponse(response);
document.body.appendChild(a);
a.click();
// 清理
setTimeout(() => {
document.body.removeChild(a);
URL.revokeObjectURL(downloadUrl);
}, 100);
return true;
} catch (error) {
console.error('下载失败:', error);
return false;
}
}
// 使用XMLHttpRequest(支持进度监控)
function downloadByXHR(url, filename, onProgress) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'blob';
if (onProgress) {
xhr.onprogress = (event) => {
if (event.lengthComputable) {
const percent = Math.round((event.loaded / event.total) * 100);
onProgress(percent);
}
};
}
xhr.onload = function() {
if (xhr.status === 200) {
const blob = xhr.response;
const downloadUrl = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = downloadUrl;
a.download = filename || getFilenameFromHeaders(xhr);
document.body.appendChild(a);
a.click();
setTimeout(() => {
document.body.removeChild(a);
URL.revokeObjectURL(downloadUrl);
}, 100);
resolve();
} else {
reject(new Error(`下载失败: ${xhr.status}`));
}
};
xhr.onerror = reject;
xhr.send();
});
}
// 辅助函数:从响应头获取文件名
function getFilenameFromHeaders(xhr) {
const disposition = xhr.getResponseHeader('Content-Disposition');
if (disposition && disposition.includes('filename=')) {
const matches = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/.exec(disposition);
if (matches != null && matches[1]) {
return matches[1].replace(/['"]/g, '');
}
}
return 'download';
}
-
优点:
完全控制下载过程
支持进度监控
可以添加自定义请求头
支持POST请求和复杂参数
可以处理大文件分块下载
支持请求取消和超时控制
-
缺点:
实现相对复杂
需要处理跨域问题
内存占用较高(大文件需注意)
旧版浏览器不支持Fetch
-
使用建议:
适合需要进度显示的大文件下载
适合需要身份验证的下载
适合动态生成内容的下载
适合需要精细控制下载过程的场景
5. Service Worker下载
高级下载方案,支持后台下载和离线访问。
代码示例
// 主线程
async function downloadByServiceWorker(url, filename) {
if ('serviceWorker' in navigator && 'BackgroundFetchManager' in window) {
try {
// 注册service worker
const registration = await navigator.serviceWorker.register('/sw.js');
// 发起后台下载
const bgFetch = await registration.backgroundFetch.fetch(
'my-download',
[url],
{
title: `下载: ${filename}`,
icons: [{ sizes: '72x72', src: '/icon.png', type: 'image/png' }],
downloadTotal: 1024 * 1024 * 10, // 预估文件大小
}
);
// 监听进度
bgFetch.addEventListener('progress', () => {
if (bgFetch.downloadTotal > 0) {
const percent = Math.round(
(bgFetch.downloaded / bgFetch.downloadTotal) * 100
);
console.log(`下载进度: ${percent}%`);
}
});
return bgFetch;
} catch (error) {
console.error('Service Worker下载失败:', error);
// 降级到普通下载
return downloadByFetch(url, filename);
}
} else {
// 浏览器不支持,降级处理
return downloadByFetch(url, filename);
}
}
// sw.js 的基本结构
self.addEventListener('install', event => {
console.log('Service Worker 安装中...');
// 预缓存关键资源
});
self.addEventListener('activate', event => {
console.log('Service Worker 激活中...');
// 清理旧缓存
});
self.addEventListener('fetch', event => {
console.log('拦截请求:', event.request.url);
// 控制网络请求
});
-
优点:
支持后台下载(用户关闭页面也可继续)
支持离线访问已下载文件
更好的用户体验
支持大文件下载
-
缺点:
实现复杂
浏览器支持有限(需HTTPS)
需要额外配置Service Worker
-
使用建议:
适合需要后台下载的场景
适合PWA应用
适合大文件下载且用户可能离开页面的情况
二、特殊场景解决方案
1. 大文件分片下载(需后端支持)
async function downloadLargeFile(url, filename, chunkSize = 1024 * 1024) {
// 1. 获取文件大小
const headResponse = await fetch(url, { method: 'HEAD' });
const totalSize = parseInt(headResponse.headers.get('Content-Length')) || 0;
// 2. 分片下载
const chunks = Math.ceil(totalSize / chunkSize);
const blobParts = [];
for (let i = 0; i < chunks; i++) {
const start = i * chunkSize;
const end = Math.min(start + chunkSize - 1, totalSize - 1);
const response = await fetch(url, {
headers: { Range: `bytes=${start}-${end}` }
});
const chunkBlob = await response.blob();
blobParts.push(chunkBlob);
// 更新进度
const progress = Math.round(((i + 1) / chunks) * 100);
console.log(`下载进度: ${progress}%`);
}
// 3. 合并并下载
const fullBlob = new Blob(blobParts);
const downloadUrl = URL.createObjectURL(fullBlob);
const a = document.createElement('a');
a.href = downloadUrl;
a.download = filename;
a.click();
// 清理
setTimeout(() => {
URL.revokeObjectURL(downloadUrl);
}, 100);
}
2. 批量下载(ZIP打包)
async function downloadMultipleFiles(urls, zipFilename = 'download.zip') {
const JSZip = await import('jszip');
const zip = new JSZip();
// 并行下载所有文件
const downloadPromises = urls.map(async (url, index) => {
try {
const response = await fetch(url);
const blob = await response.blob();
const filename = url.split('/').pop() || `file-${index}`;
zip.file(filename, blob);
} catch (error) {
console.error(`下载失败: ${url}`, error);
}
});
await Promise.all(downloadPromises);
// 生成ZIP并下载
const zipBlob = await zip.generateAsync({ type: 'blob' });
const downloadUrl = URL.createObjectURL(zipBlob);
const a = document.createElement('a');
a.href = downloadUrl;
a.download = zipFilename;
a.click();
setTimeout(() => {
URL.revokeObjectURL(downloadUrl);
}, 100);
}
三、最佳实践
1. 安全性考虑
-
始终验证下载来源,防止XSS攻击
-
对用户输入的文件名进行消毒处理
-
使用CSP(Content Security Policy)限制资源加载
-
避免直接使用用户提供的URL进行下载
2. 性能优化
-
大文件使用分片下载,避免内存溢出
-
使用Web Workers处理大文件合并
-
实现下载队列,控制并发数
-
提供取消下载的功能
3. 用户体验
-
始终显示下载进度
-
提供下载速度、剩余时间等信息
-
实现断点续传(针对大文件)
-
添加下载失败的重试机制
-
提供清晰的错误提示
4. 兼容性处理
function downloadFile(url, filename, options = {}) {
// 兼容性检测和降级策略
if ('download' in document.createElement('a')) {
// 支持download属性
return downloadByAnchor(url, filename);
} else if (navigator.msSaveBlob) {
// IE10+专用方法
return downloadForIE(url, filename);
} else {
// 降级方案
return window.open(url, '_blank');
}
}
5. 错误处理
async function safeDownload(url, filename, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
const success = await downloadByFetch(url, filename);
if (success) return;
} catch (error) {
console.warn(`下载失败 (尝试 ${i + 1}/${retries}):`, error);
if (i === retries - 1) {
throw new Error(`下载失败: ${error.message}`);
}
// 指数退避重试 避免雪崩效应
await new Promise(resolve =>
setTimeout(resolve, 1000 * Math.pow(2, i))
);
}
}
}
四、使用场景推荐
1. 简单静态文件下载
-
推荐方法: a标签下载法
-
理由: 简单直接,兼容性好
-
示例: 下载用户手册、产品图片等
2. 需要身份验证的下载
-
推荐方法: Fetch/XMLHttpRequest
-
理由: 可以添加Authorization头
-
示例: 会员专属内容下载
3. 大文件下载(>100MB)
-
推荐方法: 分片下载 + 进度显示
-
理由: 避免内存溢出,提供更好体验
-
示例: 视频文件、软件安装包
4. 批量文件下载
-
推荐方法: ZIP打包下载
-
理由: 减少请求数,方便用户管理
-
示例: 相册批量下载、文档集合
5. 后台下载/离线使用
-
推荐方法: Service Worker
-
理由: 支持后台运行,离线访问
-
示例: PWA应用资源下载
6. 旧版浏览器支持
-
推荐方法: a标签 + location.href降级
-
理由: 最大兼容性
-
示例: 企业内网系统
五、总结
前端文件下载看似简单,实则包含多种技术方案和细节考量。选择合适的方法需要综合考虑,现代Web应用推荐使用Fetch API + Blob + a标签的组合方案,它在功能、性能和兼容性之间取得了良好平衡。对于特殊需求,可结合Service Worker、Web Workers等高级API实现更强大的下载功能。
无论选择哪种方案,都应始终关注错误处理、用户反馈和资源清理,确保下载功能的稳定性和用户体验。