一、Blob 是什么?
Blob 对象表示一个不可变、原始数据的类文件对象。它的数据可以按文本或二进制的格式进行读取,也可以转换成 ReadableStream 来用于数据操作。
二、Blob URL 是什么?
通过URL.createObjectURL() 静态方法会创建一个 DOMString,其中包含一个表示参数中给出的对象的URL(Blob URL)。这个 URL 的生命周期和创建它的窗口中的 document 绑定。这个新的URL 对象表示指定的 File 对象或 Blob 对象。
URL.createObjectURL(blob) vs FileReader.readAsDataURL(file)
两者很相似
- 主要区别:
- 通过FileReader.readAsDataURL(file)可以获取一段data:base64的字符串
- 通过URL.createObjectURL(blob)可以获取当前文件的一个内存URL
- 执行时机:
- createObjectURL是同步执行(立即的)
- FileReader.readAsDataURL是异步执行(过一段时间)
- 内存使用:
- createObjectURL返回一段带hash的url,并且一直存储在内存中,直到document触发了unload事件(例如:document close)或者执行revokeObjectURL来释放。
- FileReader.readAsDataURL则返回包含很多字符的base64,并会比blob url消耗更多内存,但是在不用的时候会自动从内存中清除(通过垃圾回收机制)
- 优劣对比:
- 使用createObjectURL可以节省性能并更快速,只不过需要在不使用的情况下手动释放内存
- 如果不太在意设备性能问题,并想获取图片的base64,则推荐使用FileReader.readAsDataURL
三、Blob URL 解决了什么?
1、上传图片预览,解决了客户端无需上传二进制数据到服务端。本地生成一个二进制数据文件通过该 URL加载(js不能处理Image对象的原始字节数据)
html
<input id="upload" type="file" /><img id="preview" src="" alt="预览"/>
javascript
const upload = document.querySelector("#upload");const preview = document.querySelector("#preview");upload.onchange = function() {const file = upload.files[0]; //File对象const src = URL.createObjectURL(file);preview.src = src;};
2、Blob 对象隐藏真实的资源路径,在一定程度上可以起到数据的加密性,更多的是为了干扰爬虫,防止下载。
爬虫(spider:网络蜘蛛):是一个用脚本代替浏览器请求服务器获取服务器资源的程序。对url对应的资源进行下载分析。
将后台二进制数据转换为blob,并转换为 src=”blob:http://“ 链接,需做到以下两点:
1、服务端返回的为资源的二进制数据
2、前端接收到二进制数据后,使用 URL.createObjectURL(blobData) 方法将服务端返回的二进制数据转换为 blob 的 url 资源挂载到相应的资源对象。
注意:其实服务端将对应的资源打开读取生成二进制数据,并返回前端,会消耗对应的内存,这代价略大,而且网络传输响应的速度也不允许。不推荐,一般生产应用场景对视频m3u8格式进行切片处理。
后台code
// 加载 expressconst express = require('express')// 加载 fsconst fs = require('fs')// 创建 appconst app = express()// 静态文件托管app.use(express.static('public'))// 主页面请求app.get('/', function(req, res) {res.sendFile(__dirname + '/index.html')})app.post('/url', function(req, res) {// 读取文件fs.readFile(__dirname + '/public/test.mp4', function(err, data) {if (err) {console.log(err.stack)return}// 向前台发送二进制数据res.send(data)res.end()})})app.listen(8080, function() {console.log('Server is running……')})
前台code
'use strict'window.onload = function (evt) {// 获取元素let video = document.querySelector('.video')const xhr = new XMLHttpRequest()xhr.open('post', '/url', true)// 请求类型 buffferxhr.responseType = 'arraybuffer'xhr.onload = function () {if (xhr.status === 200 || xhr.status === 304) {// 将后台 buffer 转换为 bloblet blob = new Blob([xhr.response], {type: 'video/*'})// 创建blob链接video.src = URL.createObjectURL(blob)}}xhr.send()}
3、解决了视频呢切片无缝播放
分片的出现:
当video src属性已经变成一个Blob URL 需要等待对应的资源数据下载完成才能播放,小视频还好说,要是视频资源大一点岂不爆炸;要想解决大资源最直观体验就是使媒体文件可以边下边播,于是就有了分片;
客户端加载二进制数据:
function ajax(url, cb) {const xhr = new XMLHttpRequest();xhr.open("get", url);xhr.responseType = "blob"; // ""|"text"-字符串 "blob"-Blob对象 "arraybuffer"-ArrayBuffer对象xhr.onload = function() {cb(xhr.response);};xhr.send();}ajax('video.mp4', function(res){const src = URL.createObjectURL(res);video.src = src;})
分片带来的问题:
video标签src指向一个视频地址,视频播完了再将src修改为下一段的视频地址然后播放,这显然不符合我们无缝播放的要求。
MediaSource 解决 video视频无缝播放:
原理:用Blob URL指向一个视频二进制数据,然后不断将下一段视频的二进制数据添加拼接进去。这样就可以在不影响播放的情况下,不断的更新视频内容并播放下去,想想是不是有点流的意思出来了。
要实现这个功能我们要通过MediaSource来实现,MediaSource接口功能也很纯粹,作为一个媒体数据容器可以和HTMLMediaElement进行绑定。基本流程就是通过URL.createObjectURL创建容器的BLob URL,设置到video标签的src上,在播放过程中,我们仍然可以通过MediaSource.appendBuffer方法往容器里添加数据,达到更新视频内容的目的。
实现代码如下:
const video = document.querySelector('video');//视频资源存放路径,假设下面有5个分段视频 video1.mp4 ~ video5.mp4,第一个段为初始化视频init.mp4const assetURL = "http://www.demo.com";//视频格式和编码信息,主要为判断浏览器是否支持视频格式,但如果信息和视频不符可能会报错const mimeCodec = 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"';if ('MediaSource' in window && MediaSource.isTypeSupported(mimeCodec)) {const mediaSource = new MediaSource();video.src = URL.createObjectURL(mediaSource); //将video与MediaSource绑定,此处生成一个Blob URLmediaSource.addEventListener('sourceopen', sourceOpen); //可以理解为容器打开} else {//浏览器不支持该视频格式console.error('Unsupported MIME type or codec: ', mimeCodec);}function sourceOpen () {const mediaSource = this;const sourceBuffer = mediaSource.addSourceBuffer(mimeCodec);let i = 1;function getNextVideo(url) {//ajax代码实现翻看上文,数据请求类型为arraybufferajax(url, function(buf) {//往容器中添加请求到的数据,不会影响当下的视频播放。sourceBuffer.appendBuffer(buf);});}//每次appendBuffer数据更新完之后就会触发sourceBuffer.addEventListener("updateend", function() {if (i === 1) {//第一个初始化视频加载完就开始播放video.play();}if (i < 6) {//一段视频加载完成后,请求下一段视频getNextVideo(`${assetURL}/video${i}.mp4`);}if (i === 6) {//全部视频片段加载完关闭容器mediaSource.endOfStream();URL.revokeObjectURL(video.src); //Blob URL已经使用并加载,不需要再次使用的话可以释放掉。}i++;});//加载初始视频getNextVideo(`${assetURL}/init.mp4`);};
参考文档:
为什么视频网站的视频链接地址是blob? URL.createObjectURL()的使用方法 将后台二进制数据转换为blob,并转换为 src=”blob:http://“ 链接
