如何从IE的Javascript访问XHR responseBody(用于二进制数据)?

时间:2022-09-04 09:04:38

I've got a web page that uses XMLHttpRequest to download a binary resource.

我有一个web页面,它使用XMLHttpRequest下载二进制资源。

In Firefox and Gecko I can use responseText to get the bytes, even if the bytestream includes binary zeroes. I may need to coerce the mimetype with overrideMimeType() to make that happen. In IE, though, responseText doesn't work, because it appears to terminate at the first zero. If you read 100,000 bytes, and byte 7 is a binary zero, you will be able to access only 7 bytes. IE's XMLHttpRequest exposes a responseBody property to access the bytes. I've seen a few posts suggesting that it's impossible to access this property in any meaningful way directly from Javascript. This sounds crazy to me.

在Firefox和Gecko中,我可以使用responseText获取字节,即使bytestream包含二进制零。我可能需要使用overrideMimeType()来强迫mimetype实现。在IE中,responseText不工作,因为它似乎在第一个0处终止。如果您读取100,000字节,而字节7是二进制零,那么您将只能访问7个字节。IE的XMLHttpRequest公开一个responseBody属性以访问字节。我曾见过一些帖子,暗示用Javascript直接访问这个属性是不可能的。这听起来很疯狂。

xhr.responseBody is accessible from VBScript, so the obvious workaround is to define a method in VBScript in the webpage, and then call that method from Javascript. See jsdap for one example. EDIT: DO NOT USE THIS VBScript!!

xhr。responseBody可以从VBScript中访问,因此显而易见的解决方法是在页面中定义一个VBScript中的方法,然后从Javascript调用该方法。以jsdap为例。编辑:不要使用这个VBScript!!

var IE_HACK = (/msie/i.test(navigator.userAgent) && 
               !/opera/i.test(navigator.userAgent));   

// no no no!  Don't do this! 
if (IE_HACK) document.write('<script type="text/vbscript">\n\
     Function BinaryToArray(Binary)\n\
         Dim i\n\
         ReDim byteArray(LenB(Binary))\n\
         For i = 1 To LenB(Binary)\n\
             byteArray(i-1) = AscB(MidB(Binary, i, 1))\n\
         Next\n\
         BinaryToArray = byteArray\n\
     End Function\n\
</script>'); 

var xml = (window.XMLHttpRequest) 
    ? new XMLHttpRequest()      // Mozilla/Safari/IE7+
    : (window.ActiveXObject) 
      ? new ActiveXObject("MSXML2.XMLHTTP")  // IE6
      : null;  // Commodore 64?


xml.open("GET", url, true);
if (xml.overrideMimeType) {
    xml.overrideMimeType('text/plain; charset=x-user-defined');
} else {
    xml.setRequestHeader('Accept-Charset', 'x-user-defined');
}

xml.onreadystatechange = function() {
    if (xml.readyState == 4) {
        if (!binary) {
            callback(xml.responseText);
        } else if (IE_HACK) {
            // call a VBScript method to copy every single byte
            callback(BinaryToArray(xml.responseBody).toArray());
        } else {
            callback(getBuffer(xml.responseText));
        }
    }
};
xml.send('');

Is this really true? The best way? copying every byte? For a large binary stream that's not going to be very efficient.

这是真的吗?最好的方法?每字节复制?对于一个大的二进制流来说这不是很有效。

There is also a possible technique using ADODB.Stream, which is a COM equivalent of a MemoryStream. See here for an example. It does not require VBScript but does require a separate COM object.

还有一种可能使用ADODB的技术。流,这是一个类似于MemoryStream的COM。请看这里的一个例子。它不需要VBScript,但是需要一个单独的COM对象。

if (typeof (ActiveXObject) != "undefined" && typeof (httpRequest.responseBody) != "undefined") {
    // Convert httpRequest.responseBody byte stream to shift_jis encoded string
    var stream = new ActiveXObject("ADODB.Stream");
    stream.Type = 1; // adTypeBinary
    stream.Open ();
    stream.Write (httpRequest.responseBody);
    stream.Position = 0;
    stream.Type = 1; // adTypeBinary;
    stream.Read....          /// ???? what here
}

But that's not going to work well - ADODB.Stream is disabled on most machines these days.

但这并不能很好地解决问题。现在大多数机器上都禁用流。


In The IE8 developer tools - the IE equivalent of Firebug - I can see the responseBody is an array of bytes and I can even see the bytes themselves. The data is right there. I don't understand why I can't get to it.

在IE8的开发工具中——IE相当于Firebug——我可以看到responseBody是一个字节数组,我甚至可以看到字节本身。数据就在这里。我不明白为什么我不能得到它。

Is it possible for me to read it with responseText?

我可以用responseText来读吗?

hints? (other than defining a VBScript method)

提示?(除了定义VBScript方法之外)

7 个解决方案

#1


14  

Yes, the answer I came up with for reading binary data via XHR in IE, is to use VBScript injection. This was distasteful to me at first, but, I look at it as just one more browser dependent bit of code. (The regular XHR and responseText works fine in other browsers; you may have to coerce the mime type with XMLHttpRequest.overrideMimeType(). This isn't available on IE).

是的,我在IE中通过XHR读取二进制数据的答案是使用VBScript注入。起初,这让我很反感,但是,我把它看作是一个更依赖浏览器的代码。(常规XHR和responseText在其他浏览器中运行良好;您可能需要使用XMLHttpRequest.overrideMimeType()强制mime类型。这在IE上是不可用的。

This is how I got a thing that works like responseText in IE, even for binary data. First, inject some VBScript as a one-time thing, like this:

这就是我在IE中如何得到一个类似responseText的东西,即使是二进制数据。首先,将一些VBScript作为一次性对象注入,如下所示:

if(/msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent)) {
    var IEBinaryToArray_ByteStr_Script =
    "<!-- IEBinaryToArray_ByteStr -->\r\n"+
    "<script type='text/vbscript' language='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);
}

The JS class I'm using that reads binary files exposes a single interesting method, readCharAt(i), which reads the character (a byte, really) at the i'th index. This is how I set it up:

我正在使用的JS类读取二进制文件,它公开了一个有趣的方法,readCharAt(I),它在第I个索引中读取字符(实际上是一个字节)。我就是这样设置的:

// see doc on http://msdn.microsoft.com/en-us/library/ms535874(VS.85).aspx
function getXMLHttpRequest() 
{
    if (window.XMLHttpRequest) {
        return new window.XMLHttpRequest;
    }
    else {
        try {
            return new ActiveXObject("MSXML2.XMLHTTP"); 
        }
        catch(ex) {
            return null;
        }
    }
}

// this fn is invoked if IE
function IeBinFileReaderImpl(fileURL){
    this.req = getXMLHttpRequest();
    this.req.open("GET", fileURL, true);
    this.req.setRequestHeader("Accept-Charset", "x-user-defined");
    // my 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);
            }
        }
        // call into VBScript utility fns
        var rawBytes = IEBinaryToArray_ByteStr(binary);
        var lastChr = IEBinaryToArray_ByteStr_Last(binary);
        return rawBytes.replace(/[\s\S]/g,
                                function( match ) { return byteMapping[match]; }) + lastChr;
    };

    this.req.onreadystatechange = function(event){
        if (that.req.readyState == 4) {
            that.status = "Status: " + that.req.status;
            //that.httpStatus = that.req.status;
            if (that.req.status == 200) {
                // this doesn't work
                //fileContents = that.req.responseBody.toArray(); 

                // this doesn't work
                //fileContents = new VBArray(that.req.responseBody).toArray(); 

                // this works...
                var fileContents = convertResponseBodyToText(that.req.responseBody);

                fileSize = fileContents.length-1;
                if(that.fileSize < 0) throwException(_exception.FileLoadFailed);
                that.readByteAt = function(i){
                    return fileContents.charCodeAt(i) & 0xff;
                };
            }
            if (typeof callback == "function"){ callback(that);}
        }
    };
    this.req.send();
}

// this fn is invoked if non IE
function NormalBinFileReaderImpl(fileURL){
    this.req = new XMLHttpRequest();
    this.req.open('GET', fileURL, true);
    this.req.onreadystatechange = function(aEvt) {
        if (that.req.readyState == 4) {
            if(that.req.status == 200){
                var fileContents = that.req.responseText;
                fileSize = fileContents.length;

                that.readByteAt = function(i){
                    return fileContents.charCodeAt(i) & 0xff;
                }
                if (typeof callback == "function"){ callback(that);}
            }
            else
                throwException(_exception.FileLoadFailed);
        }
    };
    //XHR binary charset opt by Marcus Granado 2006 [http://mgran.blogspot.com] 
    this.req.overrideMimeType('text/plain; charset=x-user-defined');
    this.req.send(null);
}

The conversion code was provided by Miskun.

转换代码由Miskun提供。

Very fast, works great.

非常快,伟大的工作。

I used this method to read and extract zip files from Javascript, and also in a class that reads and displays EPUB files in Javascript. Very reasonable performance. About half a second for a 500kb file.

我使用这个方法从Javascript读取和提取zip文件,并在一个类中读取和显示Javascript中的EPUB文件。非常合理的性能。大约是500kb文件的半秒。

#2


11  

XMLHttpRequest.responseBody is a VBArray object containing the raw bytes. You can convert these objects to standard arrays using the toArray() function:

XMLHttpRequest。responseBody是一个包含原始字节的VBArray对象。您可以使用toArray()函数将这些对象转换为标准数组:

var data = xhr.responseBody.toArray();

#3


3  

I would suggest two other (fast) options:

我建议另外两种(快速)选择:

  1. First, you can use ADODB.Recordset to convert the byte array into a string. I would guess that this object is more common that ADODB.Stream, which is often disabled for security reasons. This option is VERY fast, less than 30ms for a 500kB file.

    首先,您可以使用ADODB。将字节数组转换成字符串的记录集。我猜这个对象在ADODB中更常见。流,出于安全原因经常被禁用。这个选项非常快,小于30ms的500kB文件。

  2. Second, if the Recordset component is not accessible, there is a trick to access the byte array data from Javascript. Send your xhr.responseBody to VBScript, pass it through any VBScript string function such as CStr (takes no time), and return it to JS. You will get a weird string with bytes concatenated into 16-bit unicode (in reverse). You can then convert this string quickly into a usable bytestring through a regular expression with dictionary-based replacement. Takes about 1s for 500kB.

    其次,如果无法访问Recordset组件,则有一种方法可以从Javascript访问字节数组数据。把你的xhr。responseBody到VBScript,通过任何VBScript字符串函数,如CStr(不需要时间),并将它返回给JS。您将得到一个奇怪的字符串,字节串接成16位unicode(反向)。然后,您可以通过使用基于字典的替换的正则表达式,快速地将该字符串转换为可用的bytestring。大约是500kB的1s轨道。

For comparison, the byte-by-byte conversion through loops takes several minutes for this same 500kB file, so it's a no-brainer :) Below the code I have been using, to insert into your header. Then call the function ieGetBytes with your xhr.responseBody.

为了进行比较,通过循环的字节字节转换需要几分钟的时间来完成这个相同的500kB文件,所以这是一个不用花脑筋的:)在我使用过的代码下面,插入到您的头文件中。然后用xhr.responseBody调用函数ieGetBytes。

<!--[if IE]>    
<script type="text/vbscript">

    'Best case scenario when the ADODB.Recordset object exists
    'We will do the existence test in Javascript (see after)
    'Extremely fast, about 25ms for a 500kB file
    Function ieGetBytesADO(byteArray)
        Dim recordset
        Set recordset = CreateObject("ADODB.Recordset")
        With recordset
            .Fields.Append "temp", 201, LenB(byteArray)
            .Open
            .AddNew
            .Fields("temp").AppendChunk byteArray
            .Update
        End With
        ieGetBytesADO = recordset("temp")
        recordset.Close
        Set recordset = Nothing
    End Function

    'Trick to return a Javascript-readable string from a VBScript byte array
    'Yet the string is not usable as such by Javascript, since the bytes
    'are merged into 16-bit unicode characters. Last character missing if odd length.
    Function ieRawBytes(byteArray)
        ieRawBytes = CStr(byteArray)
    End Function

    'Careful the last character is missing in case of odd file length
    'We Will call the ieLastByte function (below) from Javascript
    'Cannot merge directly within ieRawBytes as the final byte would be duplicated
    Function ieLastChr(byteArray)
        Dim lastIndex
        lastIndex = LenB(byteArray)
        if lastIndex mod 2 Then
            ieLastChr = Chr( AscB( MidB( byteArray, lastIndex, 1 ) ) )
        Else
            ieLastChr = ""
        End If
    End Function

</script>

<script type="text/javascript">
    try {   
        // best case scenario, the ADODB.Recordset object exists
        // we can use the VBScript ieGetBytes function to transform a byte array into a string
        var ieRecordset = new ActiveXObject('ADODB.Recordset');
        var ieGetBytes = function( byteArray ) {
            return ieGetBytesADO(byteArray);
        }
        ieRecordset = null;

    } catch(err) {
        // no ADODB.Recordset object, we will do the conversion quickly through a regular expression

        // initializes for once and for all the translation dictionary to speed up our regexp replacement function
        var ieByteMapping = {};
        for ( var i = 0; i < 256; i++ ) {
            for ( var j = 0; j < 256; j++ ) {
                ieByteMapping[ String.fromCharCode( i + j * 256 ) ] = String.fromCharCode(i) + String.fromCharCode(j);
            }
        }

        // since ADODB is not there, we replace the previous VBScript ieGetBytesADO function with a regExp-based function,
        // quite fast, about 1.3 seconds for 500kB (versus several minutes for byte-by-byte loops over the byte array)
        var ieGetBytes = function( byteArray ) {
            var rawBytes = ieRawBytes(byteArray),
                lastChr = ieLastChr(byteArray);

            return rawBytes.replace(/[\s\S]/g, function( match ) {
                return ieByteMapping[match]; }) + lastChr;
        }
    }
</script>
<![endif]-->

#4


1  

Thanks so much for this solution. the BinaryToArray() function in VbScript works great for me.

非常感谢这个解决方案。VbScript中的BinaryToArray()函数对我很有用。

Incidentally, I need the binary data for providing it to an Applet. (Don't ask me why Applets can't be used for downloading binary data. Long story short.. weird MS authentication that cant go thru applets (URLConn) calls. Its especially weird in cases where users are behind a proxy )

顺便说一下,我需要将二进制数据提供给Applet。(不要问我为什么applet不能用于下载二进制数据。长话短说。奇怪的身份验证,不能通过applet (URLConn)调用。它在用户支持代理的情况下尤其怪异。

The Applet needs a byte array from this data, so here's what I do to get it:

Applet需要从这些数据中得到一个字节数组,所以我要做的是:

 String[] results = result.toString().split(",");
    byte[] byteResults = new byte[results.length];
    for (int i=0; i<results.length; i++){
        byteResults[i] = (byte)Integer.parseInt(results[i]);
    }

The byte array can then converted into a bytearrayinputstream for further processing.

然后,字节数组可以转换为bytearrayinputstream以进行进一步的处理。

#5


1  

I was trying to download a file and than sign it using CAPICOM.DLL. The only way I coud do it was by injecting a VBScript function that does the download. That is my solution:

我试图下载一个文件,而不是使用CAPICOM.DLL来签署。唯一的方法是通过注入一个VBScript函数来进行下载。这是我的解决方案:

if(/msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent)) {
    var VBConteudo_Script =
    '<!-- VBConteudo -->\r\n'+
    '<script type="text/vbscript">\r\n'+
    'Function VBConteudo(url)\r\n'+
    '   Set objHTTP = CreateObject("MSXML2.XMLHTTP")\r\n'+
    '   objHTTP.open "GET", url, False\r\n'+
    '   objHTTP.send\r\n'+
    '   If objHTTP.Status = 200 Then\r\n'+
    '       VBConteudo = objHTTP.responseBody\r\n'+
    '   End If\r\n'+
    'End Function\r\n'+
    '\<\/script>\r\n';

    // inject VBScript
    document.write(VBConteudo_Script);
}

#6


1  

Thank you for this post.

谢谢你的这篇文章。

I found this link usefull:

我发现这个链接有用:

http://www.codingforums.com/javascript-programming/47018-help-using-responsetext-property-microsofts-xmlhttp-activexobject-ie6.html

http://www.codingforums.com/javascript-programming/47018-help-using-responsetext-property-microsofts-xmlhttp-activexobject-ie6.html

Specially this part:

特别这部分:

</script>
<script language="VBScript">
Function BinaryToString(Binary)
Dim I,S
For I = 1 to LenB(Binary)
S = S & Chr(AscB(MidB(Binary,I,1)))
Next
BinaryToString = S
End Function
</script>

I've added this to my htm page. Then I call this function from my javascript:

我已经把它添加到我的htm页面。然后我用javascript调用这个函数:

 responseText = BinaryToString(xhr.responseBody);

Works on IE8, IE9, IE10, FF & Chrome.

工作在IE8, IE9, IE10, FF & Chrome。

#7


1  

You could also just make a proxy script that goes to the address you're requesting & base64's it. Then you just have to pass a query string to the proxy script that tells it the address. In IE you have to manually do base64 in JS though. But this is a way to go if you don't want to use VBScript.

你也可以做一个代理脚本到你请求的地址和base64的地址。然后,您只需将查询字符串传递给代理脚本,该脚本告诉它地址。在IE中,你必须手动地用JS来做base64。但如果你不想使用VBScript,这是一种方法。

I used this for my GameBoy Color emulator.

我用这个给我的GameBoy颜色模拟器。

Here is the PHP script that does the magic:

这里有一个PHP脚本,它可以实现以下功能:

<?php
//Binary Proxy
if (isset($_GET['url'])) {
    try {
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, stripslashes($_GET['url']));
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($curl, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);
        curl_setopt($curl, CURLOPT_POST, false);
        curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 30);
        $result = curl_exec($curl);
        curl_close($curl);
        if ($result !== false) {
            header('Content-Type: text/plain; charset=ASCII');
            header('Expires: '.gmdate('D, d M Y H:i:s \G\M\T', time() + (3600 * 24 * 7)));
            echo(base64_encode($result));
        }
        else {
            header('HTTP/1.0 404 File Not Found');
        }
    }
    catch (Exception $error) { }
}
?>

#1


14  

Yes, the answer I came up with for reading binary data via XHR in IE, is to use VBScript injection. This was distasteful to me at first, but, I look at it as just one more browser dependent bit of code. (The regular XHR and responseText works fine in other browsers; you may have to coerce the mime type with XMLHttpRequest.overrideMimeType(). This isn't available on IE).

是的,我在IE中通过XHR读取二进制数据的答案是使用VBScript注入。起初,这让我很反感,但是,我把它看作是一个更依赖浏览器的代码。(常规XHR和responseText在其他浏览器中运行良好;您可能需要使用XMLHttpRequest.overrideMimeType()强制mime类型。这在IE上是不可用的。

This is how I got a thing that works like responseText in IE, even for binary data. First, inject some VBScript as a one-time thing, like this:

这就是我在IE中如何得到一个类似responseText的东西,即使是二进制数据。首先,将一些VBScript作为一次性对象注入,如下所示:

if(/msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent)) {
    var IEBinaryToArray_ByteStr_Script =
    "<!-- IEBinaryToArray_ByteStr -->\r\n"+
    "<script type='text/vbscript' language='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);
}

The JS class I'm using that reads binary files exposes a single interesting method, readCharAt(i), which reads the character (a byte, really) at the i'th index. This is how I set it up:

我正在使用的JS类读取二进制文件,它公开了一个有趣的方法,readCharAt(I),它在第I个索引中读取字符(实际上是一个字节)。我就是这样设置的:

// see doc on http://msdn.microsoft.com/en-us/library/ms535874(VS.85).aspx
function getXMLHttpRequest() 
{
    if (window.XMLHttpRequest) {
        return new window.XMLHttpRequest;
    }
    else {
        try {
            return new ActiveXObject("MSXML2.XMLHTTP"); 
        }
        catch(ex) {
            return null;
        }
    }
}

// this fn is invoked if IE
function IeBinFileReaderImpl(fileURL){
    this.req = getXMLHttpRequest();
    this.req.open("GET", fileURL, true);
    this.req.setRequestHeader("Accept-Charset", "x-user-defined");
    // my 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);
            }
        }
        // call into VBScript utility fns
        var rawBytes = IEBinaryToArray_ByteStr(binary);
        var lastChr = IEBinaryToArray_ByteStr_Last(binary);
        return rawBytes.replace(/[\s\S]/g,
                                function( match ) { return byteMapping[match]; }) + lastChr;
    };

    this.req.onreadystatechange = function(event){
        if (that.req.readyState == 4) {
            that.status = "Status: " + that.req.status;
            //that.httpStatus = that.req.status;
            if (that.req.status == 200) {
                // this doesn't work
                //fileContents = that.req.responseBody.toArray(); 

                // this doesn't work
                //fileContents = new VBArray(that.req.responseBody).toArray(); 

                // this works...
                var fileContents = convertResponseBodyToText(that.req.responseBody);

                fileSize = fileContents.length-1;
                if(that.fileSize < 0) throwException(_exception.FileLoadFailed);
                that.readByteAt = function(i){
                    return fileContents.charCodeAt(i) & 0xff;
                };
            }
            if (typeof callback == "function"){ callback(that);}
        }
    };
    this.req.send();
}

// this fn is invoked if non IE
function NormalBinFileReaderImpl(fileURL){
    this.req = new XMLHttpRequest();
    this.req.open('GET', fileURL, true);
    this.req.onreadystatechange = function(aEvt) {
        if (that.req.readyState == 4) {
            if(that.req.status == 200){
                var fileContents = that.req.responseText;
                fileSize = fileContents.length;

                that.readByteAt = function(i){
                    return fileContents.charCodeAt(i) & 0xff;
                }
                if (typeof callback == "function"){ callback(that);}
            }
            else
                throwException(_exception.FileLoadFailed);
        }
    };
    //XHR binary charset opt by Marcus Granado 2006 [http://mgran.blogspot.com] 
    this.req.overrideMimeType('text/plain; charset=x-user-defined');
    this.req.send(null);
}

The conversion code was provided by Miskun.

转换代码由Miskun提供。

Very fast, works great.

非常快,伟大的工作。

I used this method to read and extract zip files from Javascript, and also in a class that reads and displays EPUB files in Javascript. Very reasonable performance. About half a second for a 500kb file.

我使用这个方法从Javascript读取和提取zip文件,并在一个类中读取和显示Javascript中的EPUB文件。非常合理的性能。大约是500kb文件的半秒。

#2


11  

XMLHttpRequest.responseBody is a VBArray object containing the raw bytes. You can convert these objects to standard arrays using the toArray() function:

XMLHttpRequest。responseBody是一个包含原始字节的VBArray对象。您可以使用toArray()函数将这些对象转换为标准数组:

var data = xhr.responseBody.toArray();

#3


3  

I would suggest two other (fast) options:

我建议另外两种(快速)选择:

  1. First, you can use ADODB.Recordset to convert the byte array into a string. I would guess that this object is more common that ADODB.Stream, which is often disabled for security reasons. This option is VERY fast, less than 30ms for a 500kB file.

    首先,您可以使用ADODB。将字节数组转换成字符串的记录集。我猜这个对象在ADODB中更常见。流,出于安全原因经常被禁用。这个选项非常快,小于30ms的500kB文件。

  2. Second, if the Recordset component is not accessible, there is a trick to access the byte array data from Javascript. Send your xhr.responseBody to VBScript, pass it through any VBScript string function such as CStr (takes no time), and return it to JS. You will get a weird string with bytes concatenated into 16-bit unicode (in reverse). You can then convert this string quickly into a usable bytestring through a regular expression with dictionary-based replacement. Takes about 1s for 500kB.

    其次,如果无法访问Recordset组件,则有一种方法可以从Javascript访问字节数组数据。把你的xhr。responseBody到VBScript,通过任何VBScript字符串函数,如CStr(不需要时间),并将它返回给JS。您将得到一个奇怪的字符串,字节串接成16位unicode(反向)。然后,您可以通过使用基于字典的替换的正则表达式,快速地将该字符串转换为可用的bytestring。大约是500kB的1s轨道。

For comparison, the byte-by-byte conversion through loops takes several minutes for this same 500kB file, so it's a no-brainer :) Below the code I have been using, to insert into your header. Then call the function ieGetBytes with your xhr.responseBody.

为了进行比较,通过循环的字节字节转换需要几分钟的时间来完成这个相同的500kB文件,所以这是一个不用花脑筋的:)在我使用过的代码下面,插入到您的头文件中。然后用xhr.responseBody调用函数ieGetBytes。

<!--[if IE]>    
<script type="text/vbscript">

    'Best case scenario when the ADODB.Recordset object exists
    'We will do the existence test in Javascript (see after)
    'Extremely fast, about 25ms for a 500kB file
    Function ieGetBytesADO(byteArray)
        Dim recordset
        Set recordset = CreateObject("ADODB.Recordset")
        With recordset
            .Fields.Append "temp", 201, LenB(byteArray)
            .Open
            .AddNew
            .Fields("temp").AppendChunk byteArray
            .Update
        End With
        ieGetBytesADO = recordset("temp")
        recordset.Close
        Set recordset = Nothing
    End Function

    'Trick to return a Javascript-readable string from a VBScript byte array
    'Yet the string is not usable as such by Javascript, since the bytes
    'are merged into 16-bit unicode characters. Last character missing if odd length.
    Function ieRawBytes(byteArray)
        ieRawBytes = CStr(byteArray)
    End Function

    'Careful the last character is missing in case of odd file length
    'We Will call the ieLastByte function (below) from Javascript
    'Cannot merge directly within ieRawBytes as the final byte would be duplicated
    Function ieLastChr(byteArray)
        Dim lastIndex
        lastIndex = LenB(byteArray)
        if lastIndex mod 2 Then
            ieLastChr = Chr( AscB( MidB( byteArray, lastIndex, 1 ) ) )
        Else
            ieLastChr = ""
        End If
    End Function

</script>

<script type="text/javascript">
    try {   
        // best case scenario, the ADODB.Recordset object exists
        // we can use the VBScript ieGetBytes function to transform a byte array into a string
        var ieRecordset = new ActiveXObject('ADODB.Recordset');
        var ieGetBytes = function( byteArray ) {
            return ieGetBytesADO(byteArray);
        }
        ieRecordset = null;

    } catch(err) {
        // no ADODB.Recordset object, we will do the conversion quickly through a regular expression

        // initializes for once and for all the translation dictionary to speed up our regexp replacement function
        var ieByteMapping = {};
        for ( var i = 0; i < 256; i++ ) {
            for ( var j = 0; j < 256; j++ ) {
                ieByteMapping[ String.fromCharCode( i + j * 256 ) ] = String.fromCharCode(i) + String.fromCharCode(j);
            }
        }

        // since ADODB is not there, we replace the previous VBScript ieGetBytesADO function with a regExp-based function,
        // quite fast, about 1.3 seconds for 500kB (versus several minutes for byte-by-byte loops over the byte array)
        var ieGetBytes = function( byteArray ) {
            var rawBytes = ieRawBytes(byteArray),
                lastChr = ieLastChr(byteArray);

            return rawBytes.replace(/[\s\S]/g, function( match ) {
                return ieByteMapping[match]; }) + lastChr;
        }
    }
</script>
<![endif]-->

#4


1  

Thanks so much for this solution. the BinaryToArray() function in VbScript works great for me.

非常感谢这个解决方案。VbScript中的BinaryToArray()函数对我很有用。

Incidentally, I need the binary data for providing it to an Applet. (Don't ask me why Applets can't be used for downloading binary data. Long story short.. weird MS authentication that cant go thru applets (URLConn) calls. Its especially weird in cases where users are behind a proxy )

顺便说一下,我需要将二进制数据提供给Applet。(不要问我为什么applet不能用于下载二进制数据。长话短说。奇怪的身份验证,不能通过applet (URLConn)调用。它在用户支持代理的情况下尤其怪异。

The Applet needs a byte array from this data, so here's what I do to get it:

Applet需要从这些数据中得到一个字节数组,所以我要做的是:

 String[] results = result.toString().split(",");
    byte[] byteResults = new byte[results.length];
    for (int i=0; i<results.length; i++){
        byteResults[i] = (byte)Integer.parseInt(results[i]);
    }

The byte array can then converted into a bytearrayinputstream for further processing.

然后,字节数组可以转换为bytearrayinputstream以进行进一步的处理。

#5


1  

I was trying to download a file and than sign it using CAPICOM.DLL. The only way I coud do it was by injecting a VBScript function that does the download. That is my solution:

我试图下载一个文件,而不是使用CAPICOM.DLL来签署。唯一的方法是通过注入一个VBScript函数来进行下载。这是我的解决方案:

if(/msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent)) {
    var VBConteudo_Script =
    '<!-- VBConteudo -->\r\n'+
    '<script type="text/vbscript">\r\n'+
    'Function VBConteudo(url)\r\n'+
    '   Set objHTTP = CreateObject("MSXML2.XMLHTTP")\r\n'+
    '   objHTTP.open "GET", url, False\r\n'+
    '   objHTTP.send\r\n'+
    '   If objHTTP.Status = 200 Then\r\n'+
    '       VBConteudo = objHTTP.responseBody\r\n'+
    '   End If\r\n'+
    'End Function\r\n'+
    '\<\/script>\r\n';

    // inject VBScript
    document.write(VBConteudo_Script);
}

#6


1  

Thank you for this post.

谢谢你的这篇文章。

I found this link usefull:

我发现这个链接有用:

http://www.codingforums.com/javascript-programming/47018-help-using-responsetext-property-microsofts-xmlhttp-activexobject-ie6.html

http://www.codingforums.com/javascript-programming/47018-help-using-responsetext-property-microsofts-xmlhttp-activexobject-ie6.html

Specially this part:

特别这部分:

</script>
<script language="VBScript">
Function BinaryToString(Binary)
Dim I,S
For I = 1 to LenB(Binary)
S = S & Chr(AscB(MidB(Binary,I,1)))
Next
BinaryToString = S
End Function
</script>

I've added this to my htm page. Then I call this function from my javascript:

我已经把它添加到我的htm页面。然后我用javascript调用这个函数:

 responseText = BinaryToString(xhr.responseBody);

Works on IE8, IE9, IE10, FF & Chrome.

工作在IE8, IE9, IE10, FF & Chrome。

#7


1  

You could also just make a proxy script that goes to the address you're requesting & base64's it. Then you just have to pass a query string to the proxy script that tells it the address. In IE you have to manually do base64 in JS though. But this is a way to go if you don't want to use VBScript.

你也可以做一个代理脚本到你请求的地址和base64的地址。然后,您只需将查询字符串传递给代理脚本,该脚本告诉它地址。在IE中,你必须手动地用JS来做base64。但如果你不想使用VBScript,这是一种方法。

I used this for my GameBoy Color emulator.

我用这个给我的GameBoy颜色模拟器。

Here is the PHP script that does the magic:

这里有一个PHP脚本,它可以实现以下功能:

<?php
//Binary Proxy
if (isset($_GET['url'])) {
    try {
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, stripslashes($_GET['url']));
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($curl, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);
        curl_setopt($curl, CURLOPT_POST, false);
        curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 30);
        $result = curl_exec($curl);
        curl_close($curl);
        if ($result !== false) {
            header('Content-Type: text/plain; charset=ASCII');
            header('Expires: '.gmdate('D, d M Y H:i:s \G\M\T', time() + (3600 * 24 * 7)));
            echo(base64_encode($result));
        }
        else {
            header('HTTP/1.0 404 File Not Found');
        }
    }
    catch (Exception $error) { }
}
?>