一、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

  1. <input id="upload" type="file" />
  2. <img id="preview" src="" alt="预览"/>

javascript

  1. const upload = document.querySelector("#upload");
  2. const preview = document.querySelector("#preview");
  3. upload.onchange = function() {
  4. const file = upload.files[0]; //File对象
  5. const src = URL.createObjectURL(file);
  6. preview.src = src;
  7. };

2、Blob 对象隐藏真实的资源路径,在一定程度上可以起到数据的加密性,更多的是为了干扰爬虫,防止下载。

爬虫(spider:网络蜘蛛):是一个用脚本代替浏览器请求服务器获取服务器资源的程序。对url对应的资源进行下载分析。

将后台二进制数据转换为blob,并转换为 src=”blob:http://“ 链接,需做到以下两点:
1、服务端返回的为资源的二进制数据
2、前端接收到二进制数据后,使用 URL.createObjectURL(blobData) 方法将服务端返回的二进制数据转换为 blob 的 url 资源挂载到相应的资源对象。

注意:其实服务端将对应的资源打开读取生成二进制数据,并返回前端,会消耗对应的内存,这代价略大,而且网络传输响应的速度也不允许。不推荐,一般生产应用场景对视频m3u8格式进行切片处理。

后台code

  1. // 加载 express
  2. const express = require('express')
  3. // 加载 fs
  4. const fs = require('fs')
  5. // 创建 app
  6. const app = express()
  7. // 静态文件托管
  8. app.use(express.static('public'))
  9. // 主页面请求
  10. app.get('/', function(req, res) {
  11. res.sendFile(__dirname + '/index.html')
  12. })
  13. app.post('/url', function(req, res) {
  14. // 读取文件
  15. fs.readFile(__dirname + '/public/test.mp4', function(err, data) {
  16. if (err) {
  17. console.log(err.stack)
  18. return
  19. }
  20. // 向前台发送二进制数据
  21. res.send(data)
  22. res.end()
  23. })
  24. })
  25. app.listen(8080, function() {
  26. console.log('Server is running……')
  27. })

前台code

  1. 'use strict'
  2. window.onload = function (evt) {
  3. // 获取元素
  4. let video = document.querySelector('.video')
  5. const xhr = new XMLHttpRequest()
  6. xhr.open('post', '/url', true)
  7. // 请求类型 bufffer
  8. xhr.responseType = 'arraybuffer'
  9. xhr.onload = function () {
  10. if (xhr.status === 200 || xhr.status === 304) {
  11. // 将后台 buffer 转换为 blob
  12. let blob = new Blob([xhr.response], {type: 'video/*'})
  13. // 创建blob链接
  14. video.src = URL.createObjectURL(blob)
  15. }
  16. }
  17. xhr.send()
  18. }

3、解决了视频呢切片无缝播放

分片的出现:
当video src属性已经变成一个Blob URL 需要等待对应的资源数据下载完成才能播放,小视频还好说,要是视频资源大一点岂不爆炸;要想解决大资源最直观体验就是使媒体文件可以边下边播,于是就有了分片;

客户端加载二进制数据:

  1. function ajax(url, cb) {
  2. const xhr = new XMLHttpRequest();
  3. xhr.open("get", url);
  4. xhr.responseType = "blob"; // ""|"text"-字符串 "blob"-Blob对象 "arraybuffer"-ArrayBuffer对象
  5. xhr.onload = function() {
  6. cb(xhr.response);
  7. };
  8. xhr.send();
  9. }
  10. ajax('video.mp4', function(res){
  11. const src = URL.createObjectURL(res);
  12. video.src = src;
  13. })

分片带来的问题:
video标签src指向一个视频地址,视频播完了再将src修改为下一段的视频地址然后播放,这显然不符合我们无缝播放的要求。

MediaSource 解决 video视频无缝播放:
原理:用Blob URL指向一个视频二进制数据,然后不断将下一段视频的二进制数据添加拼接进去。这样就可以在不影响播放的情况下,不断的更新视频内容并播放下去,想想是不是有点流的意思出来了。

要实现这个功能我们要通过MediaSource来实现,MediaSource接口功能也很纯粹,作为一个媒体数据容器可以和HTMLMediaElement进行绑定。基本流程就是通过URL.createObjectURL创建容器的BLob URL,设置到video标签的src上,在播放过程中,我们仍然可以通过MediaSource.appendBuffer方法往容器里添加数据,达到更新视频内容的目的。

实现代码如下:

  1. const video = document.querySelector('video');
  2. //视频资源存放路径,假设下面有5个分段视频 video1.mp4 ~ video5.mp4,第一个段为初始化视频init.mp4
  3. const assetURL = "http://www.demo.com";
  4. //视频格式和编码信息,主要为判断浏览器是否支持视频格式,但如果信息和视频不符可能会报错
  5. const mimeCodec = 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"';
  6. if ('MediaSource' in window && MediaSource.isTypeSupported(mimeCodec)) {
  7. const mediaSource = new MediaSource();
  8. video.src = URL.createObjectURL(mediaSource); //将video与MediaSource绑定,此处生成一个Blob URL
  9. mediaSource.addEventListener('sourceopen', sourceOpen); //可以理解为容器打开
  10. } else {
  11. //浏览器不支持该视频格式
  12. console.error('Unsupported MIME type or codec: ', mimeCodec);
  13. }
  14. function sourceOpen () {
  15. const mediaSource = this;
  16. const sourceBuffer = mediaSource.addSourceBuffer(mimeCodec);
  17. let i = 1;
  18. function getNextVideo(url) {
  19. //ajax代码实现翻看上文,数据请求类型为arraybuffer
  20. ajax(url, function(buf) {
  21. //往容器中添加请求到的数据,不会影响当下的视频播放。
  22. sourceBuffer.appendBuffer(buf);
  23. });
  24. }
  25. //每次appendBuffer数据更新完之后就会触发
  26. sourceBuffer.addEventListener("updateend", function() {
  27. if (i === 1) {
  28. //第一个初始化视频加载完就开始播放
  29. video.play();
  30. }
  31. if (i < 6) {
  32. //一段视频加载完成后,请求下一段视频
  33. getNextVideo(`${assetURL}/video${i}.mp4`);
  34. }
  35. if (i === 6) {
  36. //全部视频片段加载完关闭容器
  37. mediaSource.endOfStream();
  38. URL.revokeObjectURL(video.src); //Blob URL已经使用并加载,不需要再次使用的话可以释放掉。
  39. }
  40. i++;
  41. });
  42. //加载初始视频
  43. getNextVideo(`${assetURL}/init.mp4`);
  44. };

参考文档:

为什么视频网站的视频链接地址是blob? URL.createObjectURL()的使用方法 将后台二进制数据转换为blob,并转换为 src=”blob:http://“ 链接