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

26

我试图在客户端加载一张图片并将服务器返回的字节以base64编码的方式传递给另一个处理程序。在XMLHttpRequest对象中,IE有一个RequestBody属性,但我似乎无法使用它,而RequestText被截断了。在Firefox中,RequestText存在,但似乎已经损坏。


经过几天的努力,我成功地做到了这一点,尽管在互联网上关于进行二进制操作的信息相当稀少。我认为这可能对其他人有用,特别是在处理数据URI时,所以我在这里详细说明了我的工作:[http://emilsblog.lerch.org/2009/07/javascript-hacks-using-xhr-to-load.html] - Emil Lerch
3个回答

12

我是这样做的:

在另一个 SO 问题的答案中提供了这个技巧,但它也与这里相关。

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

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

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

  • 虽然有一个 XMLHttpRequest.responseBody (仅限 IE!),专门用于处理二进制数据流,但这个属性可恼的是无法从 Javascript 中使用。

因此,需要将 IE 的 responseBody 属性转换成类似于 FireFox 的 responseText,并通过 mime-type coercion 来实现。 这可以使用注入的 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位置的字节。

祝你好运。

感谢Miskun提供VBScript转换逻辑。


0

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

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

在PHP或其他语言中,基本上也是这个道理。


不幸的是,在这种情况下那样做行不通 - 我需要一些COTS软件中的数据,而它没有base64选项。 - Emil Lerch

0

如果您正在使用COTS,您可以始终设置一个中间网关,在其中进行请求并转换(在这种情况下进行base64编码),以便在返回给客户端之前将其转换为更易于接受的内容。


1
我实际尝试过这个方法,但是图片是特定于用户会话的,我无法欺骗软件认为服务器是同一用户会话的一部分。 - Emil Lerch

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