如何使用 Javascript 和 XMLHttpRequest 加载二进制图像数据?

Posted

技术标签:

【中文标题】如何使用 Javascript 和 XMLHttpRequest 加载二进制图像数据?【英文标题】:How do I load binary image data using Javascript and XMLHttpRequest? 【发布时间】:2010-11-08 20:50:28 【问题描述】:

我试图加载图像客户端,并对服务器返回的字节进行 base64 编码,以便将其传递出去以执行某些处理。 IE 有 XMLHttpRequest 对象的 RequestBody 属性,但我似乎无法使用它,并且 RequestText 被截断。在 Firefox 中,RequestText 存在,但似乎已损坏。

【问题讨论】:

经过几天的努力,我能够完成这项工作,尽管互联网上进行这种二进制操作的信息相当稀缺。我认为它可能对其他人有用,尤其是在处理数据 URI 时,所以我在这里详细介绍了我的工作:http://emilsblog.lerch.org/2009/07/javascript-hacks-using-xhr-to-load.html 【参考方案1】:

这就是我的做法。

此技术在另一个 SO 问题的答案中提供,但在这里也很重要。

我不想对任何内容进行 base64 编码。我想通过 Javascript 在浏览器中下载和解析二进制文件,而不需要修改服务器来专门对其进行编码。我发现在 Firefox 中,通过overrideMimeType() 强制响应的 mimetype,我可以使用XMLHttpRequest.responseText。在 IE 上,情况有所不同,因为:

IE 上的responseText 在第一个零处截断。对于二进制流,这是一个大问题。

没有XMLHttpRequest.overrideMimeType(),强制IE将二进制流视为文本。

虽然有一个专门设计用于二进制数据流的XMLHttpRequest.responseBody(仅限 IE!),但令人抓狂的是,该属性不适用于 Javascript。

因此,需要将 IE 的 responseBody 属性转换为类似于 FireFox 中的 responseText 的内容,并带有 mime 类型的强制转换。这可以使用注入的 VBScript 来实现。

要使其跨浏览器,您只需在条件中打包特定于浏览器的逻辑。这是我用的:

// one-time code
if(/msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent)) 
    var IEBinaryToArray_ByteStr_Script =
    "<!-- IEBinaryToArray_ByteStr -->\r\n"+
    "<script type='text/vbscript'>\r\n"+
    "Function IEBinaryToArray_ByteStr(Binary)\r\n"+
    "   IEBinaryToArray_ByteStr = CStr(Binary)\r\n"+
    "End Function\r\n"+
    "Function IEBinaryToArray_ByteStr_Last(Binary)\r\n"+
    "   Dim lastIndex\r\n"+
    "   lastIndex = LenB(Binary)\r\n"+
    "   if lastIndex mod 2 Then\r\n"+
    "       IEBinaryToArray_ByteStr_Last = Chr( AscB( MidB( Binary, lastIndex, 1 ) ) )\r\n"+
    "   Else\r\n"+
    "       IEBinaryToArray_ByteStr_Last = "+'""'+"\r\n"+
    "   End If\r\n"+
    "End Function\r\n"+
    "</script>\r\n";

    // inject VBScript
    document.write(IEBinaryToArray_ByteStr_Script);



// each time you make a request for a binary resource:
var req = (function() 
    if (window.XMLHttpRequest) 
        return new window.XMLHttpRequest();
    
    else 
        try 
            return new ActiveXObject("MSXML2.XMLHTTP");
        
        catch(ex) 
            return null;
        
    
)();

var fileContents = "";
var filesize = -1;
var readByteAt = function(i)
    return fileContents.charCodeAt(i) & 0xff;
;

req.open("GET", url, true);

if(/msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent)) 
    // IE-specific logic here
    // helper to convert from responseBody to a "responseText" like thing
    var convertResponseBodyToText = function (binary) 
        var byteMapping = ;
        for ( var i = 0; i < 256; i++ ) 
            for ( var j = 0; j < 256; j++ ) 
                byteMapping[ String.fromCharCode( i + j * 256 ) ] =
                    String.fromCharCode(i) + String.fromCharCode(j);
            
        
        var rawBytes = IEBinaryToArray_ByteStr(binary);
        var lastChr = IEBinaryToArray_ByteStr_Last(binary);
        return rawBytes.replace(/[\s\S]/g,
                                function( match )  return byteMapping[match]; ) + lastChr;
    ;

    req.setRequestHeader("Accept-Charset", "x-user-defined");
    req.onreadystatechange = function(event)
        if (req.readyState == 4) 
            if (req.status == 200) 
                fileContents = convertResponseBodyToText(req.responseBody);
                fileSize = fileContents.length-1;
                // invoke a callback here, if you like...
            
            else
                alert("download failed, status " + req.status);
            
        
    ;
    req.send();

 else 
    // ff/Gecko/Webkit specific stuff here
    req.onreadystatechange = function(aEvt) 
        if (req.readyState == 4)  // completed
            if(req.status == 200) // status == OK
                fileContents = binStream.req.responseText;
                filesize = fileContents.length;
                // invoke a callback here, if you like...
            
            else 
                alert("download failed, status " + req.status);
            
        
    ;
    // coerce response type
    req.overrideMimeType('text/plain; charset=x-user-defined');
    req.send(null);

...然后调用readByte(i)获取二进制文件中ith位置的字节。

祝你好运。

Credit to Miskun 用于 VBScript 转换逻辑。

【讨论】:

【参考方案2】:

如果您使用的是 COTS,则始终可以设置一个中间网关,在该网关中发出请求并将其转换(在本例中为 base64 编码)成更可口的东西,然后再返回给客户端。

【讨论】:

我真的试过这个;但是,这些图像是特定于用户会话的,我无法伪造软件以认为服务器是同一用户会话的一部分。【参考方案3】:

您可以让服务器返回 base64 文本,而不是在客户端进行编码。

例如,(在 ASP.NET 中)对 /ImageAsBase64.ashx?file=/images/myimage.png 的请求可以被编码为读取文件,对其进行 base64 编码,并将其作为响应流式传输。

php 或其他任何东西中,它真的会是几乎相同的东西。

【讨论】:

不幸的是,在这种情况下这行不通 - 我需要来自一些 COTS 软件的数据,但它没有 base64 选项。

以上是关于如何使用 Javascript 和 XMLHttpRequest 加载二进制图像数据?的主要内容,如果未能解决你的问题,请参考以下文章

xml javascript

JS如何获取北京时间

javascript:循环中如何等待方法完成了再继续?

原生JavaScript检验URL链接是否有效(正常访问)

页面冻结在XMLHttpRequest上

使用Ajax请求加载php循环(或类似的Javascript解决方案)