How do I load binary image data using Javascript and XMLHttpRequest?

I was trying to load an image client side and base64 encode the bytes returned by the server in order to pass it off to perform some processing. IE has a RequestBody property of the XMLHttpRequest object, but I can't seem to use it, and RequestText is truncated. In Firefox, RequestText is there, but seems corrupted.


Here's how I did it.

This technique is provided in an answer to another SO question, but it's also relevant here.

I didn't want to base64 encode anything. I wanted to download and parse binary files in the browser via Javascript, without modifying the server to encode them specially. I found that in Firefox, by coercing the mimetype of the response via overrideMimeType() , I could use XMLHttpRequest.responseText . On IE, it's different because:

  • responseText on IE truncates at the first zero. For binary streams this is a big problem.

  • there is no XMLHttpRequest.overrideMimeType() , to force IE to treat binary streams as text.

  • while there is a XMLHttpRequest.responseBody (IE only!) that is specifically designed to be used with binary data streams, maddeningly that property is not usable from Javascript.

  • Therefore, the need is to convert IE's responseBody property into a thing that looks like responseText from FireFox, with the mime-type coercion. This is possible using injected VBScript.

    To make it cross-browser, you need to just pack up the browser-specific logic in a conditional. This is what I used:

    // one-time code
    if(/msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent)) {
        var IEBinaryToArray_ByteStr_Script =
        "<!-- IEBinaryToArray_ByteStr -->rn"+
        "<script type='text/vbscript'>rn"+
        "Function IEBinaryToArray_ByteStr(Binary)rn"+
        "   IEBinaryToArray_ByteStr = CStr(Binary)rn"+
        "End Functionrn"+
        "Function IEBinaryToArray_ByteStr_Last(Binary)rn"+
        "   Dim lastIndexrn"+
        "   lastIndex = LenB(Binary)rn"+
        "   if lastIndex mod 2 Thenrn"+
        "       IEBinaryToArray_ByteStr_Last = Chr( AscB( MidB( Binary, lastIndex, 1 ) ) )rn"+
        "   Elsern"+
        "       IEBinaryToArray_ByteStr_Last = "+'""'+"rn"+
        "   End Ifrn"+
        "End Functionrn"+
        "</script>rn";
    
        // 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(/[sS]/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);
    }
    

    ...then call readByte(i) to get the byte at the ith position in the binary file.

    Good luck.

    Credit to Miskun for the VBScript conversion logic.


    如果你使用的是COTS,你总是可以建立一个中间网关,在这个网关中,请求被创建和转换(在这种情况下是base64编码),然后返回给客户端,再转变为更美味的东西。


    You could have the server return base64 text, rather than doing that encoding client side.

    For example, (in ASP.NET) a request to /ImageAsBase64.ashx?file=/images/myimage.png could be coded to read the file, base64encode it, and stream it as a response.

    It's really going to be pretty much the same thing in PHP or whatever.

    链接地址: http://www.djcxy.com/p/71744.html

    上一篇: 基于Bootstrap的主题在IE10中被破解

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