html5 标签原生不支持下载,download属性仅对有效;可靠方案是用配合video.src动态获取链接,同源直接下载,跨域需fetch+blob构造url,并注意mime类型、文件后缀及服务端权限接口配合。

HTML5 <video></video> 标签本身不支持“下载”功能
浏览器原生 <video></video> 播放器默认没有下载按钮,controls 属性只提供播放/暂停/音量等基础控件,不包含下载入口。这不是你配置错了,是规范如此——W3C 明确未将下载列为标准控制项。
常见错误现象:download 属性加在 <video></video> 上完全无效(它只对 <a></a> 生效);用 JS 调 video.src 直接发起 GET 请求,结果返回 403 或跨域拒绝;或者误以为加了 crossorigin 就能触发下载,其实只是影响资源加载策略。
- 真正可下载的前提:视频资源必须允许跨域(服务端返回
Access-Control-Allow-Origin: *),且你有该资源的直链(不是动态 token 签名或 referer 限制链接) - 如果视频来自第三方平台(如 YouTube、腾讯视频),它们的
src是播放页或接口,不是视频文件地址,直接下载必然失败 - 移动端 Safari 对自动触发下载极其严格,即使满足条件,也常静默失败
用 <a download></a> 绕过限制最可靠
核心思路:不操作 <video></video>,而是把视频地址塞进一个隐藏的 <a></a> 标签,再用 JS 触发点击。这是目前兼容性最好、行为最可控的方式。
实操要点:
立即学习“前端免费学习笔记(深入)”;
-
download属性只在同源 URL 下生效;若视频是跨域资源,需先用fetch+Blob构造本地 URL,再赋给a.href - 避免直接写死路径,优先从
video.currentSrc或video.src动态取值,兼容<source></source>切换场景 - Safari 15.4+ 才支持 Blob URL 的
download,旧版需降级为打开新标签页(window.open(url))
简短示例:
const video = document.querySelector('video');
const link = document.createElement('a');
link.download = 'my-video.mp4';
// 同源情况(最简单)
if (video.src.startsWith(window.location.origin)) {
link.href = video.src;
link.click();
}
// 跨域情况(需 fetch)
else {
fetch(video.src)
.then(r => r.blob())
.then(blob => {
link.href = URL.createObjectURL(blob);
link.click();
URL.revokeObjectURL(link.href); // 及时释放
});
}
服务端配合才能解决「带权限」视频下载
如果你的视频受登录态、时间戳签名、IP 限制等保护,前端 JS 拿到的 video.src 往往是临时有效链接。此时直接下载会因签名过期或 header 缺失而失败。
必须的服务端改动:
- 提供一个专用下载接口(如
/api/video/download?id=123),由后端校验权限后,以Content-Disposition: attachment响应流式转发视频 - 禁止前端暴露原始视频 URL;所有下载请求走这个接口,由后端透传必要的认证信息(如 cookie 或 bearer token)
- 注意大文件响应超时问题,Nginx/Apache 需调高
proxy_read_timeout或启用X-Accel-Redirect
别忽略 MIME 类型和文件后缀
用户点下载后打不开,大概率不是代码问题,而是服务端没返回正确的 Content-Type,或前端没指定合理后缀。
- MP4 视频应返回
video/mp4,否则 Chrome 可能拒绝下载或保存为无扩展名文件 -
<a download="xxx"></a>中的文件名最好带后缀(如"clip.mp4"),否则 Safari 默认存为unknown - 用
Blob构造时,务必传入正确 type:new Blob([data], {type: 'video/mp4'}),否则URL.createObjectURL()生成的链接可能被识别为text/plain
复杂点在于:同一段视频可能有多种编码格式(H.264/AV1)、容器(MP4/WebM),服务端返回的 MIME 和实际内容不一致时,下载后的文件大概率无法播放——这得靠服务端日志和 ffprobe 校验,前端基本无解。











