一、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
// 加载 express
const express = require('express')
// 加载 fs
const fs = require('fs')
// 创建 app
const 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)
// 请求类型 bufffer
xhr.responseType = 'arraybuffer'
xhr.onload = function () {
if (xhr.status === 200 || xhr.status === 304) {
// 将后台 buffer 转换为 blob
let 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.mp4
const 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 URL
mediaSource.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代码实现翻看上文,数据请求类型为arraybuffer
ajax(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://“ 链接