FrankKai.github.io
FrankKai.github.io copied to clipboard
blob URL那些事儿
先看一下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
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对象会在磁盘中缓存,或者存储在内存中。 这取决于浏览器实现。
关键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
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);
// 在这里进行数据的组装并且调用服务端接口持久化存储
}
如何创建一个可用来测试的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。
不使用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