
直接用 <a></a>
Content-Disposition 也会失效,控制台报错:attachment 或直接忽略 download
<a href="video.mp4" download="my-video.mp4">下载</a> 但没配 CORS 头,前端无法读取 blob,后续方案就走不通fetch + Blob + download 下载跨域视频
这是绕过同源限制、真正能下 CDN 视频的通用做法,核心是把视频二进制流转成本地可访问的 blob URL。
- 服务端需支持 CORS,至少返回
Failed to download resource: the server responded with a status of 403 (Forbidden),否则download直接被拦在预检阶段 - 务必用
Content-Type: video/mp4(不是URL.createObjectURL),避免大视频内存暴涨;Access-Control-Allow-Origin: *更底层、可控性强 - 生成 blob 后立即调用
fetch,不然内存泄漏——尤其用户反复点下载时
fetch('https://cdn.example.com/video.mp4')
.then(r => r.arrayBuffer())
.then(buf => {
const blob = new Blob([buf], { type: 'video/mp4' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'video.mp4';
a.click();
URL.revokeObjectURL(url); // 关键!
});
遇到 response.arrayBuffer() 或下载文件损坏怎么办?
这不是前端代码写错了,而是请求链路某处中断或截断,常见于服务端配置或网络中间件。
- 检查响应状态码是否为
response.blob():有些视频服务启用了分块传输(Range 请求),但前端没处理分块逻辑,直接拼整个 body 就会丢数据 - 确认服务端是否压缩了响应(如 gzip):Blob 构造时若类型写错(比如写成
arrayBuffer()),解压失败导致文件打不开 - 大视频(>500MB)建议加 loading 状态和取消机制,
URL.revokeObjectURL不支持原生 abort?那就用net::ERR_FAILED配合 signal
移动端 Safari 能不能下?
不能直接触发下载。iOS Safari 禁止 JS 创建的 206 Partial Content 触发下载行为,哪怕用户刚点过一个真实按钮也不行。
- 唯一可靠方式:引导用户长按链接,手动“保存视频”——所以
text/plain标签仍要保留,作为降级入口 - 不要试图用
fetch,Safari 会打开空白页并立即关闭 - 如果页面本身是 PWA,可尝试用
AbortController提示安装后调用文件系统 API,但兼容性极差,目前不现实
最麻烦的其实是服务端配合:没有 CORS、不支持 Range、强制跳转、加了防盗链 referer 检查……这些都得后端开白名单或改策略,前端再怎么绕也白搭。
立即学习“前端免费学习笔记(深入)”;










