Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/466.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何从IE中的Javascript访问XHR响应库(二进制数据)?_Javascript_Xmlhttprequest - Fatal编程技术网

如何从IE中的Javascript访问XHR响应库(二进制数据)?

如何从IE中的Javascript访问XHR响应库(二进制数据)?,javascript,xmlhttprequest,Javascript,Xmlhttprequest,我有一个用来下载二进制资源的网页 在Firefox和Gecko中,我可以使用responseText获取字节,即使ByTestStream包含二进制零。我可能需要使用overrideMimeType()强制mimetype来实现这一点。不过,在IE中,responseText不起作用,因为它似乎在第一个零处终止。如果读取100000个字节,而字节7是二进制零,则只能访问7个字节。IE的XMLHttpRequest公开了一个responseBody属性来访问字节。我看到一些帖子表明,直接从Java

我有一个用来下载二进制资源的网页

在Firefox和Gecko中,我可以使用responseText获取字节,即使ByTestStream包含二进制零。我可能需要使用
overrideMimeType()
强制mimetype来实现这一点。不过,在IE中,responseText不起作用,因为它似乎在第一个零处终止。如果读取100000个字节,而字节7是二进制零,则只能访问7个字节。IE的XMLHttpRequest公开了一个
responseBody
属性来访问字节。我看到一些帖子表明,直接从Javascript以任何有意义的方式访问此属性是不可能的。这听起来很疯狂

xhr.responseBody
可以从VBScript访问,因此显而易见的解决方法是在网页的VBScript中定义一个方法,然后从Javascript调用该方法。参见示例编辑:不要使用此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('');
但这不会很好地起作用——现在大多数机器上都禁用了ADODB.Stream


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

我可以用responseText阅读吗


暗示?(除了定义VBScript方法)

是的,我在IE中通过XHR读取二进制数据的答案是使用VBScript注入。起初,这让我很反感,但是,我把它看作是更多依赖于浏览器的代码。 (常规的XHR和responseText在其他浏览器中可以正常工作;您可能必须使用强制mime类型。这在IE上不可用)

这就是我在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);
}
这是由Miskun提供的

非常快,效果很好


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

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

顺便说一句,我需要二进制数据来提供给Applet。(不要问我为什么小程序不能用于下载二进制数据。长话短说..奇怪的MS身份验证无法通过小程序(URLConn)调用。在用户在代理后的情况下尤其奇怪)

小程序需要一个来自此数据的字节数组,因此我要做的是:

 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]);
    }
String[]results=result.toString().split(“,”);
byte[]byteResults=新字节[results.length];
对于(int i=0;i我建议另外两个(快速)选项:

  • 首先,你可以使用 ADODB.Recordset将字节数组转换为字符串。我想这个对象比ADODB.Stream更常见,因为安全原因,它经常被禁用。这个选项非常快,对于500kB的文件不到30ms

  • 其次,如果记录集组件不可访问,则有一个从Javascript访问字节数组数据的技巧。将xhr.responseBody发送到VBScript,通过任何VBScript字符串函数(如CStr)传递它(不需要时间),并将其返回到JS。您将得到一个奇怪的字符串,其中字节连接为16位unicode(反向)。然后,您可以通过基于字典的替换,通过正则表达式将此字符串快速转换为可用的ByTestString。500kB大约需要1s

  • 为了进行比较,同样的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]-->
    

    您也可以创建一个代理脚本,该脚本指向您请求的地址&base64的it。然后,您只需向代理脚本传递一个查询字符串,该脚本告诉您地址。但在IE中,您必须在JS中手动执行base64。但如果您不想使用VBScript,这是一种方法

    我用这个来做手术

    下面是发挥神奇作用的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) { }
    }
    ?>
    

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

    var data = xhr.responseBody.toArray();
    

    我试图下载一个文件,然后使用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);
    }
    
    if(/msie/i.test(navigator.userAgent)&&!/opera/i.test(navigator.userAgent)){
    var VBConteudo_脚本=
    “\r\n”+
    “\r\n”+
    '函数VBConteudo(url)\r\n'+
    'Set objHTTP=CreateObject(“MSXML2.XMLHTTP”)\r\n'+
    'objHTTP.open“GET”,url,False\r\n'+
    'objHTTP.send\r\n'+
    '如果objHTTP.Status=200,则\r\n'+
    'VBConteudo=objHTTP.responseBy\r\n'+
    '如果结束\r\n'+
    '结束函数\r\n'+
    “\\r\n”;
    //注入VBScript
    编写(VBConteudo_脚本);
    }
    
    谢谢你发这篇文章

    我发现这个链接很有用:

    特别是本部分:

    </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>
    

    适用于IE8、IE9、IE10、FF和Chrome。

    在其他浏览器中,您使用什么读取二进制数据?我看到你的问题引用了一个
    getBuffer
    函数。那是什么?仅AFAIK IE支持
    响应库
    。那么您使用了什么呢?我在IE中使用了MSXML2.XMLHTTP,在非IE中使用了XMLHttpRequest()。在非IE浏览器中,我可以使用responseText获取字节流。显然IE认为它是一个字符串,因此在IE中,超过第一个零,responseText[i]返回“undefined”。但FF3.5却并非如此。它只是起作用。但是……那么它可能是一个dif
    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);
    }
    
    </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>
    
     responseText = BinaryToString(xhr.responseBody);