从 XHR 请求中获取 BLOB 数据

Posted

技术标签:

【中文标题】从 XHR 请求中获取 BLOB 数据【英文标题】:Getting BLOB data from XHR request 【发布时间】:2011-12-22 18:32:55 【问题描述】:
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://static.reddit.com/reddit.com.header.png', true);

xhr.responseType = 'arraybuffer';

xhr.onload = function(e) 
  if (this.status == 200) 
    var uInt8Array = new Uint8Array(this.response);
    var byte3 = uInt8Array[4]; 

    var bb = new WebKitBlobBuilder();
    bb.append(xhr.response);
    var blob = bb.getBlob('image/png'); 
    var base64 = window.btoa(blob);
    alert(base64);

  
;

xhr.send();

基本上,我在这里要做的是检索图像,并将其转换为 base64。

通过阅读 cmets here,它指出:

“当然。在获取资源作为 ArrayBuffer 后,从 它。一旦你有了它,你可以直接对文件/blob进行base64编码 window.btoa()FileReader.readAsDataURL()。”

但是,blob 只是 [object blob],而我需要从图像中获取二进制文件,以便将其转换为 base64 并使用数据将其显示在 img 标签中。

有人知道如何实现吗?

提前谢谢你!

【问题讨论】:

我觉得你用 XHR 获取图像日期很奇怪......它甚至可以跨源工作吗?你的域名在 reddit 的 Access-Control-Allow-Origin 列表中吗? 这只是一个例子,实际的域是localhost 【参考方案1】:

不要在 Chrome 中使用 BlobBuilder(在 OSX Chrome、Firefox 12、Safari 6、ios Chrome、iOS Safari 中测试):

ex1 : http://jsfiddle.net/malraux/xGUsu/(原理)

ex2:http://jsfiddle.net/xGUsu/78/(使用完整示例)

var xhr = new XMLHttpRequest();
xhr.open('GET', 'doodle.png', true);

xhr.responseType = 'arraybuffer';

// Process the response when the request is ready.
xhr.onload = function(e) 
  if (this.status == 200) 
    // Create a binary string from the returned data, then encode it as a data URL.
    var uInt8Array = new Uint8Array(this.response);
    var i = uInt8Array.length;
    var binaryString = new Array(i);
    while (i--)
    
      binaryString[i] = String.fromCharCode(uInt8Array[i]);
    
    var data = binaryString.join('');

    var base64 = window.btoa(data);

    document.getElementById("myImage").src="data:image/png;base64," + base64;
  
;

xhr.send();

注意这段代码现在已经超过 7 年了。 虽然它在大多数浏览器中仍然可以正常工作,但这里是基于 @TypeError 建议的更新版本这只适用于更现代的浏览器iOS Safari 可能例外(可能支持也可能不支持responseType = 'blob' - 请务必进行测试!):

var xhr = new XMLHttpRequest();
xhr.open('get', 'doodle.png', true);

// Load the data directly as a Blob.
xhr.responseType = 'blob';

xhr.onload = () => 
  document.querySelector('#myimage').src = URL.createObjectURL(this.response);
;

xhr.send(); 

【讨论】:

xhr.responseType = 'blob' 没有在 chrome 中实现,它需要是一个数组缓冲区。见code.google.com/p/chromium/issues/detail?id=52486 谢谢!正是我想要的! Scott A,你是我眼中的神。确认它有效:) 有没有办法在不逐字节迭代数据的情况下做到这一点? @metadaddy:请注意,Blob 构造函数仅在最新的浏览器中可用。 @ScottA:好的,我之前没有意识到我们加载的文件是_not / is_different_from base64。图像在其他东西中,BLOB,“存储为单个实体的二进制数据集合”。所以你必须先转换成base64。明白了。【参考方案2】:

您可以获取Blob 并使用window.URL.createObjectURL。这可以防止构建巨大的字符串并多次复制所有内容。

var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://i.imgur.com/sBJOoTm.png', true);

xhr.responseType = 'blob';

xhr.onload = function(e) 
  if (this.status == 200) 
    var blob = this.response;
    document.getElementById("myImage").src = window.URL.createObjectURL(blob);
  
;

xhr.onerror = function(e) 
  alert("Error " + e.target.status + " occurred while receiving the document.");
;

xhr.send();
<img id="myImage">

示例(相同代码):http://jsfiddle.net/ysangkok/sJxXk/86/。适用于 Firefox 和 Chrome 25+。以及除 Opera Mini 之外的所有其他浏览器:http://caniuse.com/#search=Blob

【讨论】:

Blob 构造函数仅在最新的浏览器中可用。例如,如果您必须支持 IE8 或 9,您将无法使用它。 @JanusTroelsen 如果您必须支持其中一种浏览器,这绝对没有任何意义(无论如何它都比 Firefox 使用的百分比更大)。 :-) 顺便说一句,IE10 仅 @JanusTroelsen 现在你在做一个政治论点,而不是一个实际的论点。对于我们这些必须在现实世界中谋生的人来说,非免费与免费不是一个选择,但如果你能够支持自己并坚持无情的立场,那么你就会拥有更多的力量。我个人有一个非常大的企业客户,直到 18 个月前还在内部使用 IE6,而现在使用的是 IE8。我无法告诉他们我只会支持 Chrome 或 Firefox,因为它们是免费软件。 @JanusTroelsen,为什么不直接将responseType 设置为blob,而不是通过arraybuffer 间接进行呢?见此代码:jsfiddle.net/RE9YJ 在 AngularJS 中,将 responseType: 'blob' 作为配置选项传递给 $http【参考方案3】:

XMLHttpRequest

var xmlhttp = new XMLHttpRequest();
xmlhttp.open('GET', 'http://RestServiceURL-Returns Image', true);
xmlhttp.setRequestHeader('Content-type','application/x-www-form-urlencoded');
xmlhttp.responseType = 'arraybuffer/blob';
xmlhttp.send();

以 3 种方式创建 blob 图像。

window.URL.createObjectURL FileReader (caniuse)

Base64String

xmlhttp.onload = function() 
    var blob = new Blob([this.response], type: 'image/png'); 
    console.log(blob, blob.type, this.response, typeof this.response);  

    var image = document.getElementById('my-image');

    1)image.src = window.URL.createObjectURL(blob);

    2)var fileReader = new window.FileReader();
    fileReader.readAsDataURL(blob);
    fileReader.onloadend = function()  
    image.src = fileReader.result;
    

    3)var base64String = btoa(String.fromCharCode.apply(null, new Uint8Array(this.response)));
    image.src = 'data:image/png;base64,'+base64String;
;

将 ArrayBuffer 转换为 Blob 到 ArrayBuffer

1)var dataView = new DataView(arrayBuffer);
var blob = new Blob([dataView],  type: mimeString );


2)fileReader.readAsArrayBuffer(blob);
var arrayBuffer;
fileReader.onload = function() 
    arrayBuffer = this.result;
;

【讨论】:

然后用于将文件保存在 IE 11 或 Egde 中:window.navigator.msSaveOrOpenBlob(blob, 'filename.pdf');【参考方案4】:

与Janus Troelsen 建议的解决方案相同,并添加了承诺...

注意!使用createObjectURL时 - 不要忘记致电revokeObjectURL

//  Load blob (promise)
function loadBlob( url )
    return new Promise( (resolve, reject) => 
        const xhr = new XMLHttpRequest();
        xhr.open('GET', url, true);
        xhr.responseType = 'blob';        
        xhr.onload  = () => resolve(xhr.response);
        xhr.onerror = () => reject(xhr.statusText);        
        xhr.send();
    );


//  Create image from blob (createObjectURL)
function imageFromBlob( blob ) 
    const img = new Image();
    img.onload = () => URL.revokeObjectURL(img.src);
    img.src = URL.createObjectURL(blob);    
    return img;



//  Create image from blob if loaded successfully
loadBlob('https://unsplash.it/960/540?random')
    .then( blob => 
        document.body.appendChild( imageFromBlob(blob) );      
    )
    .catch( error => 
        console.log('Could not load image');
    )
    


//  Alternate version adding promise to xhr
//  if you like to trigger xhr.send() yourself
function xhrBlob(url)
    const xhr = new XMLHttpRequest();
    xhr.open('GET', url, true);
    xhr.responseType = 'blob';        
    xhr.promise = new Promise((resolve, reject) => 
        xhr.onload  = () => resolve(xhr.response);
        xhr.onerror = () => reject(xhr.statusText);  
    );
    xhr.load = ( onsuccess = () => , onerror = () =>  ) =>  
        xhr.promise.then(onsuccess).catch(onerror);
        xhr.send();
        return xhr;
    
    return xhr;



//  Using load callbacks
xhrBlob('https://unsplash.it/960/540?random')
    .load( 
        //  on sussess
        blob => 
            document.body.appendChild( imageFromBlob(blob) );      
        ,
        //  on error
        error => 
            console.log('Could not load image');
        
    );
    
 //  Using promise (delayed)
const image = xhrBlob('https://unsplash.it/960/540?random');

    //  Promise handlers
    image.promise
    .then( blob => 
        document.body.appendChild( imageFromBlob(blob) );      
    )
    .catch( error => 
        console.log('Could not load image');
    );
 
 //  Load image (will trigger promise handlers)
 setTimeout(image.load, 3000);
img 
  width: 100%;

【讨论】:

以上是关于从 XHR 请求中获取 BLOB 数据的主要内容,如果未能解决你的问题,请参考以下文章

video文件转blob

xhr 请求响应不应从磁盘缓存轨道中选择

XHR post请求下载文件

XHR 请求在 Scrapy 中失败,但在 python-requests 中有效

如果已经以相同的请求运行,则取消订阅/取消角度4中的现有HTTP / XHR调用

TinyMCE 在 http 请求 xhr ajax 生成的页面中不起作用