【问题标题】:Using raw image data from ajax request for data URI使用来自 ajax 请求的原始图像数据获取数据 URI
【发布时间】:2013-11-30 21:14:04
【问题描述】:

我正在尝试使用 Ajax 和数据 URI 的组合来加载 JPEG 图像并通过单个 HTTP 请求提取其 EXIF 数据。我正在修改一个库 (https://github.com/kennydude/photosphere) 来执行此操作;目前该库使用两个 HTTP 请求来设置图像的来源并获取 EXIF 数据。

获取 EXIF 有效,没问题。但是,我在使用来自 ajax 请求的原始数据作为图像源时遇到了困难。

技术小测试的源代码:

<!DOCTYPE html>
<html>
<head>
<script type='text/javascript'>

function init()
{
    // own ajax library - using it to request a test jpg image
    new Ajax().sendRequest
    (
        "/images/photos/badger.jpg",
         { method : "GET",
            callback: function(xmlHTTP)
            {

                var encoded = btoa (unescape(encodeURIComponent(xmlHTTP.responseText)));
                var dataURL="data:image/jpeg;base64,"+encoded;

                document.getElementById("image").src = dataURL;
            }
        }
    );
}

</script>
<script type="text/javascript" src="http://www.free-map.org.uk/0.6/js/lib/Ajax.js"></script>
</head>
<body onload='init()'>
<img id="image" alt="data url loaded image" />
</body>
</html>

我得到了看起来像合理的 jpeg 数据发回,原始数据的长度(以字节为单位)和 base64-encoded-then-unencoded-again 的原始数据是相同的。但是,在 Firefox (25) 和 Chrome (31)(当前版本)上设置图像 src 的尝试均失败 - chrome 显示“损坏的图像”图标,表明 src 是无效格式。

我使用这个 mozilla 页面获取有关 base64 编码/解码的信息:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Base64_encoding_and_decoding

知道可能出了什么问题吗?环顾四周,我可以创建 base64 编码的图像服务器端,但它可以像这样在客户端完成吗?一方面,base64 编码服务器端明显增加了数据大小,这个练习的全部目的是减少从服务器传输的数据量,以及请求的数量。

谢谢, 尼克

【问题讨论】:

    标签: javascript ajax image base64


    【解决方案1】:

    谢谢你。我对此进行了更多的挖掘,结果证明至少在当前版本的 Firefox 和 Chrome 上有一个解决方案(编辑:IE10 也可以)。您可以使用 XMLHttpRequest2 并使用类型化数组 (Uint8Array)。以下代码有效:

    <!DOCTYPE html>
    <html>
    <head>
    <script type='text/javascript'>
    
    function init()
    {
        var xmlHTTP = new XMLHttpRequest();
        xmlHTTP.open('GET','/images/photos/badger.jpg',true);
    
        // Must include this line - specifies the response type we want
        xmlHTTP.responseType = 'arraybuffer';
    
        xmlHTTP.onload = function(e)
        {
    
            var arr = new Uint8Array(this.response);
    
    
            // Convert the int array to a binary string
            // We have to use apply() as we are converting an *array*
            // and String.fromCharCode() takes one or more single values, not
            // an array.
            var raw = String.fromCharCode.apply(null,arr);
    
            // This works!!!
            var b64=btoa(raw);
            var dataURL="data:image/jpeg;base64,"+b64;
            document.getElementById("image").src = dataURL;
        };
    
        xmlHTTP.send();
    }
    
    </script>
    </head>
    <body onload='init()'>
    <img id="image" alt="data url loaded image" />
    </body>
    </html>
    

    基本上,您要求一个二进制响应,然后创建数据的 8 位无符号 int 视图,然后再将其转换回(二进制友好)字符串 String.fromCharCode()。 apply 是必要的,因为 String.fromCharCode() 不接受数组参数。然后你使用 btoa(),创建你的数据 url,然后它就可以工作了。

    以下资源对此很有用:

    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays?redirectlocale=en-US&redirectslug=JavaScript%2FTyped_arrays

    http://www.html5rocks.com/en/tutorials/file/xhr2/

    尼克

    【讨论】:

    • 很棒的帖子。我现在终于可以发表评论了,但是如果您因为文件太大而遇到堆栈溢出错误,请在下面查看我的答案!
    【解决方案2】:

    尼克的回答效果很好。但是当我对一个相当大的文件执行此操作时,我在

    上遇到了堆栈溢出
    var raw = String.fromCharCode.apply(null,arr);
    

    以块的形式生成原始字符串对我来说效果很好。

    var raw = '';
    var i,j,subArray,chunk = 5000;
    for (i=0,j=arr.length; i<j; i+=chunk) {
       subArray = arr.subarray(i,i+chunk);
       raw += String.fromCharCode.apply(null, subArray);
    }
    

    【讨论】:

    • 另外,为了支持 IE9,您必须包含来自该项目 github.com/inexorabletash/polyfill 的 typedarray.js。这定义了 Uint8Array。
    • 这个技巧是让它在 iOS 和 Android 上工作所必需的。感谢您发布此内容!
    【解决方案3】:

    我在使用上述ArrayBuffer -&gt; String -&gt; Base64 方法时遇到了问题,但遇到了另一种使用Blob 的方法,效果很好。 不是一种将原始数据转换为 Base 64 的方式(如标题所示),但它一种显示原始图像数据的方式(如实际问题中所示):

    var xhr = new XMLHttpRequest();
    xhr.responseType = 'arraybuffer';
    xhr.onload = function() {
        var blb = new Blob([xhr.response], {type: 'image/png'});
        var url = (window.URL || window.webkitURL).createObjectURL(blb);
        image.src = url;
    }
    
    xhr.open('GET', 'http://whatever.com/wherever');
    xhr.send();
    

    所有功劳归于this fiddle 的作者 Jan Miksovsky。我只是偶然发现了它,并认为它会为本次讨论提供有用的补充。

    【讨论】:

    • 干得好!我不得不做一些测试,但现在,这对我来说就像一个魅力!除此之外,如果您右键单击以在另一个选项卡中打开文件,与 ArrayBuffer -> String -> Base64 方法相比,您没有这么长的 URL。 (顺便说一句:不推荐使用“webkitURL”。改用“URL”)
    【解决方案4】:

    现代 ES6 驱动的图像下载解决方案: (不指定图像类型)

    async function downloadImageFromUrl(url) {    // returns dataURL
      const xmlHTTP = new XMLHttpRequest();
      xmlHTTP.open('GET', url, true);
      xmlHTTP.responseType = 'blob';
      const imageBlob = await new Promise((resolve, reject) => {
        xmlHTTP.onload = e => xmlHTTP.status >= 200 && xmlHTTP.status < 300 && xmlHTTP.response.type.startsWith('image/') ? resolve(xmlHTTP.response) : reject(Error(`wrong status or type: ${xmlHTTP.status}/${xmlHTTP.response.type}`));
        xmlHTTP.onerror = reject;
        xmlHTTP.send();
      });
      return blobToDataUrl(imageBlob);
    }
    
    function blobToDataUrl(blob) { return new Promise(resolve => {
      const reader = new FileReader();    // https://developer.mozilla.org/en-US/docs/Using_files_from_web_applications
      reader.onload = e => resolve(e.target.result);
      reader.readAsDataURL(blob);
    })}
    

    用法:

    downloadImageFromUrl('https://a.b/img.png').then(console.log, console.error)
    

    【讨论】:

      【解决方案5】:

      我已经在这个问题上工作了两天,因为我需要一个解决方案来根据从 Microsoft Graft 收到的原始数据呈现用户的 Outlook 个人资料图片。我已经实施了上述所有解决方案,但没有成功。然后我找到了这个git: get base64 raw data of image from responseBody using jquery ajax

      就我而言,我只是将"data:image/png;base64," 替换为"data:image/jpg;base64,"

      它就像一个魅力。

      【讨论】:

        【解决方案6】:

        您必须在服务器端进行 base64 编码,因为 responseText 被视为字符串,而服务器发送的响应数据是二进制的。

        【讨论】:

        • 我做了更多的挖掘工作,结果证明有一个解决方案,至少在现代 HTML5 浏览器中(适用于当前的 Firefox/Chrome)。
        猜你喜欢
        • 1970-01-01
        • 2011-12-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-02-13
        • 1970-01-01
        • 1970-01-01
        • 2016-04-06
        相关资源
        最近更新 更多