浏览器驯服录<之>一键保存视频到本地
请听题:给定一个视频链接和一个按钮,如何实现点击按钮把视频保存到本地?
零、太长不看版
1、手动实现
- 第 1 步:以视频 URL 手动发起
GET
请求,把响应数据转为blob
格式的临时 URL;
- 第 2 步:给
<a>
标签设置download
属性,且href
属性值为第 1 步生成的临时 URL;
-
第 3 步:在
<a>
标签上触发点击,以触发浏览器的下载操作。
下方压缩包中是调试代码,运行方法参见压缩包中的 README.md
。
2、现成轮子
感谢掘友@修苟小狗的提醒,GitHub 上有现成可用的轮子:download。从源码来看,实现思路和本文大致相同,不想手动折腾的掘友们可以直接拿来用。
一、要做什么
最近遇到一个 CMS 需求,形态如下图所示:
左侧是视频,视频资源信息(URL、封面图、时长、大小等)来自服务端接口;右侧有一个「下载视频」按钮。用户希望点一下按钮就把视频保存到本地。
首先来看实现细节。
二、如何实现
-
点击按钮时,发起一个 GET 请求,请求路径为视频 URL,并把响应数据转为
blob
格式。不同的网络请求 API 会有不同的设置方式,此处以 axios 为例:
axios({ url: videoUrl, method: 'GET', responseType: 'blob', timeout, // 可以根据视频体积动态地设置超时参数。 }).then(blob => { ... })
-
接收到响应后,给
blob
文件生成一个临时 URL(形如:blob:``http://127.0.0.1:5500/c708f2b5-327e-4581-a267-4d8eb7f343b8
):const tempUrl = window.URL.createObjectURL(blob)
-
创建一个
a
标签,href
属性的值为上一步生成的临时 URL;download
属性的值为字符串,代表保存时的文件名,可为空。const aTag = document.createElement('a') aTag.href = tempUrl aTag.download = ''
-
插入
a
标签并触发点击事件:document.body.appendChild(aTag) aTag.click() document.body.removeChild(aTag)
- 此时,浏览器界面会唤起文件保存窗口,用户可将视频保存到本地。
三、原理解析
在网页中点击超链接时,如果目标资源在浏览器中可预览,那么浏览器默认会直接打开资源进行预览,如 .html
、.png
、.mp4
、.mp3
等类型。
如果浏览器无法预览,则会弹出保存窗口,引导用户保存到本地,交由操作系统来处理,如 .zip
等类型。
但 <a>
标签支持一个 download 属性
。当我们设置了 download
(即使值为空字符串),标签被点击时,浏览器会将操作视为下载行为。
那么只要 <a>
标签有 download
属性就高枕无忧了吗?非也。
想要让 download
生效,使用场景需要符合以下条件之一:
- 资源地址与当前页面同源
-
href
值是data:
或blob:
格式
我司的 CDN 静态资源与页面地址不同源,所以需要单独发起一次 GET
请求,把视频转为 blob
格式,并赋值给一个动态插入的 <a>
标签,再触发点击,促使浏览器执行文件保存操作。
四、结语
把文件保存到本地,是一个直觉上很容易实现的功能。但浏览器总是有那么一些小小的傲娇,我们需要借助一些小手段才能把它驯服。
感谢阅读。
如需勘误、讨论、建议,热烈欢迎留言。
转载自:https://juejin.cn/post/7169911036856762381