从XHR请求获取BLOB数据

52
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格式。

从阅读评论此处中得知:

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

然而,blob只是[object blob],而我需要获取图像的二进制数据,以便将其转换为base64格式,并使用data在img标签中显示它。

有人知道如何实现吗?

提前致谢!


我觉得你使用XHR获取图像数据非常奇怪...这样跨域访问真的有效吗?你的域名是否在Reddit的Access-Control-Allow-Origin列表中? - Rudie
1
那只是一个例子,实际域名是localhost。 - Nick Bennet
4个回答

74

在Chrome浏览器中不要使用BlobBuilder(已在OSX Chrome、Firefox 12、Safari 6、iOS Chrome和iOS Safari中测试):

示例1:http://jsfiddle.net/malraux/xGUsu/ (原理)

示例2: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(); 

1
xhr.responseType = 'blob' 在Chrome中没有实现,需要改为arraybuffer。请参见http://code.google.com/p/chromium/issues/detail?id=52486。 - Nick Bennet
2
谢谢!这正是我在寻找的!Scott A,你在我眼中是一个神。已确认它可以工作 :) - Nick Bennet
1
有没有一种方法可以在不逐字节迭代数据的情况下完成这个操作? - metadaddy
我可以使用上面给出的代码来执行xhr.open('POST', 'doodle.png', true)吗? - V_PULL
1
@ScottA:好的,我之前没有意识到我们加载的文件与base64不同。图片是以BLOB的形式存储的,即“作为单个实体存储的二进制数据集合”。因此,您必须先将其转换为base64格式。明白了。 - Hugolpz
显示剩余4条评论

53
你可以获取一个 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


2
Blob构造函数仅适用于最新的浏览器。例如,如果您需要支持IE8或9,则无法使用它。 - Scott A
2
@JanusTroelsen 如果你需要支援其中一種瀏覽器(而且這部分的使用率比 Firefox 還要高),那麼這就毫無意義了。順道一提,IE10 的使用率只有不到 4%,因此你基本上是在主張開發者應該完全忽略 IE;如果你看的是 NetApplications 而不是 StatCounter,那麼 IE 8+9 在桌面上的占比為 44%。話雖如此,你的 Blob 解決方案在以下瀏覽器上都不能運作:OS X 上的 Safari 6.0.2、OS X 上的 Firefox 12,以及我的 iPhone 4s 上的 Safari 和 Chrome。 - Scott A
2
@JanusTroelsen 现在你正在提出一个政治论点,而不是实际的论点。对于我们这些必须在现实世界中工作谋生的人来说,非自由软件和自由软件并不是一个选择,但如果你能够自给自足并坚持不可原谅的立场,那就更加强大了。我个人曾经有一个非常大的企业客户,仍然在18个月前内部使用IE6,现在已经升级到IE8。我没有选择告诉他们我只支持Chrome或Firefox,因为它们是免费软件。 - Scott A
1
@JanusTroelsen,为什么不直接将responseType设置为blob,而是通过arraybuffer间接实现呢?请参考此代码:http://jsfiddle.net/RE9YJ/ - Pacerier
在AngularJS中,将{responseType: 'blob'}作为配置选项传递给$http - z0r
显示剩余5条评论

9

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();

三种方式创建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或Edge中的代码:window.navigator.msSaveOrOpenBlob(blob, 'filename.pdf'); - Oleksii Kyslytsyn

7

Janus Troelsen建议的相同解决方案,但添加了promise...

注意!使用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%;
}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接