我有一个使用XMLHttpRequest来下载二进制资源的网页。
在Firefox和Gecko中,即使字节流包含二进制零,我也可以使用responseText来获取字节。我可能需要强迫模仿overrideMimeType()才能实现。但是,在IE中,responseText不起作用,因为它似乎在第一个零处终止。如果读取100,000个字节,并且字节7为二进制零,则只能访问7个字节。IE的XMLHttpRequest公开了一个responseBody属性来访问字节。我看到一些帖子暗示不可能直接从Javascript以任何有意义的方式访问此属性。这对我来说听起来很疯狂。
overrideMimeType()
responseBody
xhr.responseBody _是_从VBScript访问,因此明显的解决方法是在VBScript在网页中定义的方法,然后从JavaScript调用该方法。 编辑:请勿使用此VBScript!
xhr.responseBody
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,它与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 }
但这不能很好地工作-如今,大多数计算机上都禁用了ADODB.Stream。
在IE8开发人员工具(相当于IE的Firebug)中,我可以看到responseBody是字节数组,甚至可以看到字节本身。数据 就在那里 。我不明白为什么我不能做到这一点。
我可以用responseText读取它吗?
提示?(除了定义VBScript方法之外)
是的,我想通过IE中的XHR读取二进制数据的答案是使用VBScript注入。一开始这对我来说很令人讨厌,但是,我将其视为仅仅是一个依赖于浏览器的代码。(常规XHR和responseText在其他浏览器中可以正常工作;您可能必须使用来强制MIME类型。IEXMLHttpRequest.overrideMimeType()]上不提供此功能)。
XMLHttpRequest.overrideMimeType()
这就是我得到的东西responseText即使在二进制数据中也可以在IE中工作的方式。首先,一次性注入一些VBScript,如下所示:
responseText
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); }
我正在使用的读取二进制文件的JS类公开了一个有趣的方法readCharAt(i),该方法读取第i个索引处的字符(实际上是一个字节)。这是我的设置方式:
readCharAt(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); }
转换码被提供Miskun。
效果很好。
我使用此方法从Javascript读取和提取zip文件,并且还在使用Javascript读取和显示EPUB文件的类中使用了该方法。性能非常合理。500kb文件大约需要半秒钟。