音视频下载
方案
- fetch
- XMLHttpRequest(XHR)
- iframe
- a标签
1、iframe和a标签都是调用浏览器默认的下载行为
- 因为都是触发浏览器的下载行为,因此都无法监听下载进度
2、自定义文件名,都是通过a标签的
download属性
fetch
优点:
- 支持显示下载进度。
- 通过
reponse.body.getReader()【流读取器】
- 通过
- 可以自定义文件名。
- 通过a标签的
download属性
- 通过a标签的
- 支持并发控制。
- 灵活性强,可做各种预处理
缺点:
- 对跨域资源要求高(需要 CORS 支持
Access-Control-Allow-Origin)。 - 无法绕过浏览器下载限制(需用户交互触发)
- 需要考虑浏览器兼容性问题
async function downloadFile(url, filename, onProgress) {
const res = await fetch(url);
if (!res.ok) throw new Error('下载失败');
const contentLength = res.headers.get('content-length');
const total = parseInt(contentLength, 10);
let loaded = 0;
// 读取进度
const reader = res.body.getReader();
const chunks = [];
while (true) {
const { done, value } = await reader.read();
if (done) break;
chunks.push(value);
loaded += value.length;
if (onProgress) onProgress(loaded / total);
}
const blob = new Blob(chunks);
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = filename;
a.click();
URL.revokeObjectURL(a.href);
}
// 示例调用:
downloadFile('https://example.com/video.mp4', 'my-video.mp4', progress => {
console.log(`下载进度:${(progress * 100).toFixed(2)}%`);
});
XMLHttpRequest(XHR)
优点:
- 兼容性更好,更适合低版本浏览器
- 支持进度监听
progress事件 - 支持自定义文件名
缺点:
- api使用上,相对于fetch复杂一点
- 没有async/await的语法糖
function downloadWithXHR(url, filename, onProgress) {
const xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'blob';
xhr.onprogress = (e) => {
if (e.lengthComputable && onProgress) {
onProgress(e.loaded / e.total);
}
};
xhr.onload = () => {
const blob = xhr.response;
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = filename;
a.click();
URL.revokeObjectURL(a.href);
};
xhr.onerror = () => {
console.error('下载失败');
};
xhr.send();
}
// 示例调用
downloadWithXHR('https://example.com/audio.mp3', 'my-audio.mp3', progress => {
console.log(`进度: ${(progress * 100).toFixed(2)}%`);
});
下载原理流程
- XMLHttpRequest 发起请求 → 开始下载到内存
- 监听 onprogress 事件 → 显示下载进度
- 下载完成后 → 创建 Blob 对象
- 创建
<a>标签 → 触发浏览器保存对话框 - 用户选择保存位置 → 从内存写入磁盘
注意首先是下载到浏览器内存中,最后才将内存写入磁盘
iframe
优点:
-
兼容低版本浏览器
-
简单直接,触发浏览器默认下载行为
-
可结合使用a标签嵌入iframe中,来自定义文件名
缺点:
- 无法监听下载进度
- 无法自定义文件名,要搭配a标签
function downloadByIframe(url) {
const iframe = document.createElement('iframe');
iframe.style.display = 'none';
iframe.src = url;
document.body.appendChild(iframe);
// 清理
setTimeout(() => document.body.removeChild(iframe), 5000);
}
iframe下载的特点
当使用a标签下载的时候,当文件比较大的时候,文件下载需要比较久,这时候如果切换页面,当文件下载完之后,会突然弹出文件保存框,会影响用户体验
-
1、使用隐藏的 iframe 来触发下载
-
2、将 Blob 的 MIME 类型设置为 application/octet-stream,强制浏览器下载而不是预览
-
3、在 iframe 内部创建下载链接并触发,这样可以避免在主页面弹出对话框
a标签
优点:
- 简单直接,触发浏览器默认下载行为
- 支持自定义文件名(使用
download属性)。 - 使用 a.download 属性会触发浏览器的保存对话框
缺点:
- 不能监听下载进度
- 无法针对复杂场景,如无法控制并发、暂停等
function downloadByATag(url, filename) {
const a = document.createElement('a');
a.href = url;
a.download = filename; // 自定义文件名
a.style.display = 'none';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
// 调用示例
downloadByATag('https://example.com/video.mp4', 'video-custom-name.mp4');
分片下载
下载管理操作
- 实现下载文件url,如何实现下载进度、暂停、恢复、缓存
- 分片下载怎么实现断点续传?已下载的文件怎么存储?
- 使用了XMLHttpRequest 发起下载请求,当关闭页面,或者离开页面了,如何存储已经下载好的进度,当后面打开页面的时候,可以恢复进度
- 1、可使用IndexedDB 来存储下载进度和部分数据
下载考虑点
-
对于大文件,使用分片下载
-
大文件下载考虑
- 使用
stream,避免内存一次性加载
- 使用
-
并发数控制下载
- 推荐 3~5 个同时下载,防止浏览器并发限制,防止一次性下载卡死
-
下载的暂停与恢复
- 可通过
AbortController实现中断
- 可通过
-
下载失败重试
- 当下载失败时,重新调用下载方法
-
自定义文件名,通过a标签方式
-
Web Worker 多线程下载