如何在Javascript中将API调用的图像二进制转换为数据URI?

10
我正在使用的Google API仅以二进制数据形式传输图像。
我完全不知道如何将其转换为数据URI以显示它,感谢任何帮助!
我所说的调用是这个API调用
正如你所看到的,它说:
服务器返回照片的字节。
对于这个调用(它是一个扩展),我使用chrome_ex_oauth方法。也许我需要添加一些内容到头文件中,以获取真正的二进制数据,而不是像现在一样的字符串...
我需要做的是将结果二进制转换为数据URI,以便我可以显示它。

好的,我从XHR请求中获取到了这个

enter image description here

现在,我对二进制编码不是很了解。我猜这是以某种方式编码的二进制数据?我尝试将其放入btoa和其他base64编码器中,但所有东西都会抛出错误。我尝试使用不同的overrideMimeType,"response"以一些奇怪的方式改变,但没有任何接受数据的东西。所以现在我有了这段代码:
var nxhr = new XMLHttpRequest();
nxhr.onreadystatechange = function (data) {
    if (nxhr.readyState == 4) {
        console.log(nxhr);
    }
};
nxhr.open(method, url, true);
nxhr.setRequestHeader('GData-Version', '3.0');
nxhr.setRequestHeader('Authorization', oauth.getAuthorizationHeader(url, method, params));
nxhr.send('Data to send');

还有其他人有办法将这个对我来说不可理解的响应转换成数据URI吗?

感谢任何帮助。


二进制...成千上万的丑陋字符...你知道二进制只有两个值,对吧? - Joseph Marikle
3
FileReader?听起来像是[标签:Java],而不是[标签:JavaScript]。能否展示一个SSCCE呢?否则,我真的不知道你在问什么。 - Matt Ball
@Joseph,也许他只是真的讨厌看到一和零的样子。 - Kevin Ennis
1
HTML5有FileReader;-)是的Joseph,我知道:)但是将二进制数据放入字符串中,你会看到超过两个值,对吧? - Luke
当您将二进制放入字符串中时,它只显示从一系列1字节字符值中获取的字符。如果您在某个地方存储了一个4字节整数,并尝试通过4 * 1字节的数组读取它,则会得到4个不同的字符。字符串表示无关紧要,位序列才是关键。 - user613857
它抛出了一个错误?什么错误?不管怎样,也许试着将其作为二进制文件读取 - Ry-
5个回答

17

经过一些测试,这是我的答案:

要使用<img>标签简单显示图像,您必须首先用Base64对结果二进制进行编码。 您可以通过两种不同的方式来完成此操作:

  1. 使用Javascript:使用Base64编码器函数,例如此函数。 在对结果二进制数据进行编码之后,您可以使用以下方法使用<img>标签显示图像:<img src="data:image/*;base64,[BASE64 ENCODED BINARY]" />。 您必须将[BASE64 ENCODED BINARY]替换为实际图像的已编码二进制。 我假设您已经知道如何通过Javascript更改HTML元素属性,这很容易将已编码二进制放入<img>标记的src属性中。

  2. 使用PHP(我个人偏好):一旦您向API提交GET请求,它将返回二进制。 只需使用PHP的base64_encode()函数即可。

    <img src="data:image/*;base64,<?php echo base64_encode($result); ?>" />

其中,$result变量是您从API调用中获取的内容。 您可以使用PHP cURL库。

希望这可以帮助到您。


谢谢,虽然我已经尝试过一个Base64编码器(它告诉我一个错误“只能处理ASCII代码”),但我将尝试你的。就像我说的那样,这是一个扩展程序(针对Chrome),我只能通过JS访问它。此外,我必须依赖于chrome_ex_oauth来为我进行Ajax调用。在尝试您的解决方案时,我想请您查看oauth,也许有些东西使返回的数据无法满足我的需求。http://code.google.com/chrome/extensions/tut_oauth.html - Luke
很抱歉告诉你,这个不起作用。无论是btoa还是其他base64编码器都无法处理我接收到的内容。我尝试在XHR对象上使用overrideMimeType并使用不同的字符集,我几乎尝试了所有方法。 - Luke
好的,让我们深入代码。你能给我一个代码片段,在那里你执行GET请求来获取一张照片吗?如果你不想在这里显示它,你可以将它发送到我的电子邮件,在我的个人资料中。 - user613857
据我所知,您没有使用OAuth的sendSignedRequest方法。您获得的XMLHttpRequest响应确实是我预期的。假设您有<img id="imageTag" src="" />。请使用document.getElementById("imageTag").src = "data:image/*;base64," + btoa(nxhr.response);更改图像源。 - user613857
是的,我使用XMLHttpRequest,因为我可以设置头信息等等。它的工作方式与SendSignedRequest相同。问题是,btoa(nxhr.response)会抛出错误... - Luke
显示剩余5条评论

5

好的,我找到了解决方案...

首先,请求必须将返回类型覆盖为x-user-defined

xhr.overrideMimeType('text\/plain; charset=x-user-defined');

在此之后,数据将不受浏览器的影响。

使用以下Base64编码器

Base64 = {

            // private property
            _keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",

            encodeBinary: function (input) {
                var output = "";
                var bytebuffer;
                var encodedCharIndexes = new Array(4);
                var inx = 0;
                var paddingBytes = 0;

                while (inx < input.length) {
                    // Fill byte buffer array
                    bytebuffer = new Array(3);
                    for (jnx = 0; jnx < bytebuffer.length; jnx++)
                        if (inx < input.length)
                            bytebuffer[jnx] = input.charCodeAt(inx++) & 0xff; // throw away high-order byte, as documented at: https://developer.mozilla.org/En/Using_XMLHttpRequest#Handling_binary_data
                        else
                            bytebuffer[jnx] = 0;

                    // Get each encoded character, 6 bits at a time
                    // index 1: first 6 bits
                    encodedCharIndexes[0] = bytebuffer[0] >> 2;
                    // index 2: second 6 bits (2 least significant bits from input byte 1 + 4 most significant bits from byte 2)
                    encodedCharIndexes[1] = ((bytebuffer[0] & 0x3) << 4) | (bytebuffer[1] >> 4);
                    // index 3: third 6 bits (4 least significant bits from input byte 2 + 2 most significant bits from byte 3)
                    encodedCharIndexes[2] = ((bytebuffer[1] & 0x0f) << 2) | (bytebuffer[2] >> 6);
                    // index 3: forth 6 bits (6 least significant bits from input byte 3)
                    encodedCharIndexes[3] = bytebuffer[2] & 0x3f;

                    // Determine whether padding happened, and adjust accordingly
                    paddingBytes = inx - (input.length - 1);
                    switch (paddingBytes) {
                        case 2:
                            // Set last 2 characters to padding char
                            encodedCharIndexes[3] = 64;
                            encodedCharIndexes[2] = 64;
                            break;
                        case 1:
                            // Set last character to padding char
                            encodedCharIndexes[3] = 64;
                            break;
                        default:
                            break; // No padding - proceed
                    }
                    // Now we will grab each appropriate character out of our keystring
                    // based on our index array and append it to the output string
                    for (jnx = 0; jnx < encodedCharIndexes.length; jnx++)
                        output += this._keyStr.charAt(encodedCharIndexes[jnx]);
                }
                return output;
            }
        };

这里有Mozilla发布的神奇内容,它导致我无法正确编码内容。

bytebuffer[jnx] = input.charCodeAt(inx++) & 0xff

最终的代码看起来会像这样...
oauth.authorize(function () {
    var method = "GET", params = {}, url = photo.href;

    var nxhr = new XMLHttpRequest();
    nxhr.onreadystatechange = function (data) {
        if (nxhr.readyState == 4) {
            console.log("<img src='data:image/*;base64," + Base64.encodeBinary(nxhr.response) + "' />");
        }
    };
    nxhr.open(method, url, true);
    nxhr.setRequestHeader('GData-Version', '3.0');
    nxhr.setRequestHeader('Authorization', oauth.getAuthorizationHeader(url, method, params));
    nxhr.overrideMimeType('text\/plain; charset=x-user-defined'); 
});

P.S. 如果您直接将"data:image/*"放入浏览器窗口中,它将下载文件并无法打开它。但是如果您将其直接放入img src中,它可以正常工作!


1
我无法使这个解决方案起作用。你能提供一个可行的 fiddle 吗? - Hugolpz
你好 @luke,能否提供一下代码片段? - SagarPPanchal

2

1

据我所知,自IE7以来,Internet Explorer支持img data。 IE7应该足够老了。此外,btoa()函数是Gecko DOM的一部分,不适用于IE。 - user613857
@Ozbekov:不仅仅是Gecko,Safari和Chrome也支持它。编辑以包含我本意的内容。 - Ry-
@Ozbekov:为什么我编辑了我的答案并提到了包含另一种替代函数的第二个答案。 - Ry-
2
嘿,伙计们,别担心,只需使用Chrome浏览器,不需要其他任何东西,它将是一个Chrome扩展程序。哈,这不是很棒吗,不用担心IE了吗? :) - Luke
@chunk_split: 这里与UTF-8无关(图像不是UTF-8,btoa也不需要UTF-8)。要使用btoa,您需要一个字符串,该字符串来自将图像的字节编码为Latin-1(例如,从字节数组而不是XHR文本中使用String.fromCharCode(...bytes))。或者至少这是2011年的方法。现在对于一般的base64编码,我会避免通过二进制字符串进行操作,并使用一个操作Uint8Array的包(这个包不知何故没有像btoa一样内置到浏览器中)。 - Ry-
显示剩余3条评论

1
I know这是一个非常古老的问题,但由于这与通过XHR传输图像和二进制数据有关,对我来说秘诀在于两个重要步骤:
第一步:确保您使用自定义用户定义的字符集以防止浏览器更改内容。例如:
xhr.overrideMimeType('text\/plain; charset=x-user-defined');

步骤2: 使用 String.fromCharCode(response.charCodeAt(...) & 0xff) 来解决 btoa() 无法处理二进制字符串的问题。这受到了 @https://gist.github.com/graylikeme/867848 的启发(目前对我来说还是黑魔法)。

最终结果: 这是一个简化版本,通常您也会这样做,因为您需要发送授权标头或其他内容,但在此示例中未包含。

<div id="output-el"></div>
<script>
    var xhr = new XMLHttpRequest();
    xhr.addEventListener('readystatechange', function() {
        var outputEl = document.getElementById('xhr-output');

        // Done?
        if (this.readyState === 4) {

            // Make sure this is an image.
            var contentType = this.getResponseHeader('content-type');
            if (contentType !== null && contentType.indexOf('image/') === 0) {
                // Prepare binary response so we can base64 encode it (and btoa() function doesn't freak out).
                // Thanks to: https://gist.github.com/graylikeme/867848
                var response = this.responseText;
                var binary = '';
                for(i=0; i < response.length; i++) {
                    binary += String.fromCharCode(response.charCodeAt(i) & 0xff);
                }

                // Drop into data URI.
                var encoded = btoa(binary);
                outputEl.innerHTML = `<img alt="image preview" src="data:${contentType};base64,${encoded}">`;

            } else {
                console.log('Got a non-image response:', this.responseText);
            }
        }
    });

    // Make sure browser doesn't alter the response before we handle it.
    xhr.overrideMimeType('text\/plain; charset=x-user-defined');
    xhr.open('GET', 'https://example.com/path/to/some/image.jpg');
    xhr.send();
</script>

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