Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

blob URL那些事儿 #138

Open
FrankKai opened this issue Mar 21, 2019 · 5 comments
Open

blob URL那些事儿 #138

FrankKai opened this issue Mar 21, 2019 · 5 comments

Comments

@FrankKai
Copy link
Owner

FrankKai commented Mar 21, 2019

先看一下blob URL长什么样:

blob:http://localhost:9090/39b60422-26f4-4c67-8456-7ac3f29115ec

http/https协议前加了一个blob:,是不是看起来很神奇,其实它不仅看起来神奇,在前端开发领域用处也十分广泛。

blob对象在前端开发中是非常常见的,下面我将列举几个应用场景:

  • canvas toDataURL后的base64格式属性,会超出标签属性值有最大长度的限制
  • <input type="file" />上传文件之后的File对象,最初只想在本地留存,时机合适再上传到服务器

blob仅仅是一种数据对象,在上述2个场景中,都需要比较重要的一个API,那就是window.URL.createObjectURL()。所以我们在介绍blob对象之余,也会对这个关键的API进行介绍。

所以文章主要分为以下几个部分:

  • Blob对象知识点
  • canvas toDataURL场景下的blob
  • <input type="file" />场景下的blob
  • 关键API window.URL.createObjectURL()
  • Blob,File对象如何转换为base64字符串
  • 如何创建一个可用来测试的Blob对象
  • 不使用canvas的情况下将image的url转换为blob url
@FrankKai
Copy link
Owner Author

FrankKai commented May 24, 2019

Blob对象知识点

  • 将一个ArrayBuffer转换为Blob对象时,可以指定一个MIME类型。例如const blob = new Blob([new Uint8Array(arrayBuffer)], { type: 'audio/AMR' });
  • Blob对象是一个类文件对象,熟知的FIle就是继承自Blob类的。
  • 使用Blob()构造器,可以将非blob对象转换为Blob。
  • 可以使用slice方法获取到Blob切片,而且可以修改MIME类型。
  • 还有3个Working Draft方法,stream,text,ArrayBuffer。
  • Blob对象会在磁盘中缓存,或者存储在内存中。 这取决于浏览器实现。

@FrankKai FrankKai changed the title 如何理解blob对象? blob URL那些事儿 May 24, 2019
@FrankKai
Copy link
Owner Author

FrankKai commented May 25, 2019

关键API window.URL.createObjectURL()

  • URL.createObjectURL(object) object可以包括FIle,Blob和MediaSource对象
  • URL.createObjectURL()的生命周期仅仅局限于创建它的window对象的document
  • 需要通过URL.revokeObjectURL(objectURL)通知浏览器,不再持有文件的引用从而释放内存,若不释放,会在document销毁时自动释放

提醒:尤其第三点,涉及到了内存管理,一定要注意释放内存

疑问

  • URL的生命周期在vue组件中如何表现?

vue的单文件组件共有一个document,这也是它被称为单页应用的原因,因此可以在组件间直接通过blob URL进行通信。
在vue-router采用hash模式的情况下,页面间的路由跳转,不会重新加载整个页面,所以URL的生命周期非常强力,因此在跨页面(非新tab)的组件通信,也可以使用blob URL。
需要注意的是,在vue的hash mode模式下,需要更加注意通过URL.revokeObjectURL()进行的内存释放,在大量使用blob URL的情况下,可能会撑爆浏览器内存

组件发出blob URL
<label for="background">上传背景</label>
<input type="file" style="display: none"
           id="background" name="background"
           accept="image/png, image/jpeg" multiple="false"
           @change="backgroundUpload"
>
backgroundUpload(event) {
  const fileBlobURL = window.URL.createObjectURL(event.target.files[0]);
  this.$emit('background-change', fileBlobURL);
  // this.$bus.$emit('background-change', fileBlobURL);
},
组件接收blob URL
<BackgroundUploader @background-change="backgroundChangeHandler"></BackgroundUploader>
// this.$bus.$on("background-change", backgroundChangeHandler);
backgroundChangeHandler(url) {
    // some code handle blob url...
},
  • 如何查看当前document中的所有blob URL?

暂时没有找到方法获取所有的blob URL,但是可以通过sessionStorage和localStorage进行标记,通过key进行更新或者访问。
若是没有新tab的纯单页应用的跨组件通信,可以存储在sessionStorage或者localStorage;若是存在新的tab,blob URL跨tab已经失效,需要存储最原始的数据类型,使用时再进行转换。
需要注意的是,在一个blob URL使用结束后,需要从storage和内存中同时删除。

提醒:如果你的项目会涉及到多个tab间的通信,blob URL在另一个tab中是无效的。可以存储原始数据,使用时再转换为blob URL

@FrankKai
Copy link
Owner Author

FrankKai commented Jul 10, 2019

Blob,File对象如何转换为base64字符串

本地预览仅仅适用于浏览器预览,当需要持久化存储时,就需要将其上传到云存储上,现在用处比较广泛的是OSS、七牛云等公有云,私有云没接触过,应该是大同小异。
如果是上传到云存储,一个仅在当前document内生命周期的blob url就不生效了,那么就需要上传有效的数据上去,就我目前接触的项目来说,我们是将文件最终转换成base64格式,通过接入sdk接口的服务端接口实现上传。
那么问题来了,如果是canvas,可以通过toDataURL()转换为base64,第三方的canvas库也提供相同的api。可以将blob url转换为base64吗?那么如何将File,Blob对象转换为base64格式?

可以将blob url转换为base64吗?

不可以。

如何将File,Blob对象转换为base64格式?

用到一个神奇的对象:Reader对象
用到一个神奇的方法:readAsDataURL(blob)
当文件完成读取并成功转换后,会触发loadend事件,同时会在Reader实例的result属性中存储base64格式的数据。
若是比较抽象,可以看下面的示例:

transferBlobFileToBase64(file){
    const reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onloadend = async function() {
        const fileBase64 = reader.result;
        // 下面的接口是一个接入了七牛sdk的接口,通过base64str接收base64格式数据,返回一个hash
        const { hash } = await uploadQiNiu({ base64Str: fileBase64 });
        // 根据环境对hash进行拼接,生成一个可访问的url并且存储到持久层(mysql, elastic search, tablestore, whatever)
    };
}

若需要上传以后传递到其他方法进行数据组装,可以采用下面的方法:

async transferBlobFileToBase64(file) {
  return new Promise((resolve) => {
    const reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onloadend = async function() {
      const fileBase64 = reader.result;
      const { hash } = await uploadQiNiu({ base64Str: fileBase64 });
      resolve(`${pageConfig.QINIU_IMG_URL}/${hash}`);
    };
  });
},
async serverAPIRevoke(file) {
  const uploadedFile = await this.transferBlobFileToBase64(file);
  // 在这里进行数据的组装并且调用服务端接口持久化存储
}

@FrankKai
Copy link
Owner Author

FrankKai commented Sep 23, 2019

如何创建一个可用来测试的Blob对象

var newBlob = new Blob(array, options);
// array是个数组:数组元素可以是ArrayBuffer,ArrayBufferView, Blob, USVString 

普通的 ‘foo’也可以作为生成Blob对象。

var blob = new Blob(['foo'],{type:'text/plain'})
blob.text().then((data)=>{console.log(data)})// 'foo'

这样就生成一个最简易的blob对象了,可以用来测试一些api。

@FrankKai
Copy link
Owner Author

不使用canvas的情况下将image的url转换为blob url

fetch api

fetch(url).then(response => response.blob()).then((blob)=>{
    const urlCreator = window.URL || window.webkitURL;
    const imageUrl = urlCreator.createObjectURL( blob );
    const img = new Image();
    img.crossOrigin = 'Anonymous'; // CORS
    img.src = imageUrl;
})

XHR

// Simulate a call to Dropbox or other service that can
// return an image as an ArrayBuffer.
const xhr = new XMLHttpRequest();

// Use JSFiddle logo as a sample image to avoid complicating
// this example with cross-domain issues.
xhr.open( "GET", url, true );

// Ask for the result as an ArrayBuffer.
xhr.responseType = "arraybuffer";

xhr.onload = function( e ) {
    // Obtain a blob: URL for the image data.
    const arrayBufferView = new Uint8Array( this.response );
    const blob = new Blob( [ arrayBufferView ], { type: "image/jpeg" } );
    const urlCreator = window.URL || window.webkitURL;
    const imageUrl = urlCreator.createObjectURL( blob );
    const img = new Image();
    img.crossOrigin = 'Anonymous'; // CORS
    img.src = imageUrl;
};

xhr.send();

问题源于此帖:https://www.v2ex.com/t/648393#reply3
XHR思路源于:https://jsfiddle.net/Jan_Miksovsky/yy7Zs
可测试图片:https://img.thedailybeast.com/image/upload/c_crop,d_placeholder_euli9k,h_1439,w_2560,x_0,y_0/dpr_1.5/c_limit,w_1044/fl_lossy,q_auto/v1492791705/articles/2013/01/17/why-the-lakers-kobe-bryant-and-wife-vanessa-are-staying-together/130114-Samuels-Kobe-divorce-01_pfkz0r

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant