跳到主要内容

音视频下载

方案​

  • fetch
  • XMLHttpRequest(XHR)
  • iframe
  • a标签

1、iframe和a标签都是调用浏览器默认的下载行为

  • 因为都是触发浏览器的下载行为,因此都无法监听下载进度

2、自定义文件名,都是通过a标签的download属性

fetch​

优点:

  • 支持显示下载进度。
    • 通过 reponse.body.getReader() 【流读取器】
  • 可以自定义文件名。
    • 通过a标签的download属性
  • 支持并发控制。
  • 灵活性强,可做各种预处理

缺点:

  • 对跨域资源要求高(需要 CORS 支持 Access-Control-Allow-Origin)。
  • 无法绕过浏览器下载限制(需用户交互触发)
  • 需要考虑浏览器兼容性问题
js
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的语法糖
js
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 发起请求 → 开始下载到内存
  1. 监听 onprogress 事件 → 显示下载进度
  2. 下载完成后 → 创建 Blob 对象
  3. 创建 <a>标签 → 触发浏览器保存对话框
  4. 用户选择保存位置 → 从内存写入磁盘

注意首先是下载到浏览器内存中,最后才将内存写入磁盘

iframe​

优点:

  • 兼容低版本浏览器

  • 简单直接,触发浏览器默认下载行为

  • 可结合使用a标签嵌入iframe中,来自定义文件名

缺点:

  • 无法监听下载进度
  • 无法自定义文件名,要搭配a标签
js
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 属性会触发浏览器的保存对话框

缺点:

  • 不能监听下载进度
  • 无法针对复杂场景,如无法控制并发、暂停等
js
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 多线程下载