我有一个使用XMLHttpRequest下载二进制资源的网页.
在Firefox和Gecko中,我可以使用responseText来获取字节,即使字节流包含二进制零.我可能需要强制使用mimetype overrideMimeType()
来实现这一点.但是,在IE中,responseText不起作用,因为它似乎终止于第一个零.如果读取100,000个字节,而字节7是二进制零,则只能访问7个字节.IE的XMLHttpRequest公开了一个responseBody
访问字节的属性.我已经看到一些帖子暗示直接从Javascript以任何有意义的方式访问这个属性是不可能的.这听起来很疯狂.
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(''); 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开发人员工具 - 相当于Firebug的IE中 - 我可以看到responseBody是一个字节数组,我甚至可以看到字节本身.数据就在那里.我不明白为什么我无法达到它.
我可以用responseText读取它吗?
提示?(除了定义VBScript方法)
是的,我想通过IE中的XHR读取二进制数据的答案就是使用VBScript注入.起初这对我来说是令人讨厌的,但是,我认为它只是一个与浏览器相关的代码.(常规XHR和responseText在其他浏览器中工作正常;您可能必须强制使用mime类型XMLHttpRequest.overrideMimeType()
.这在IE上不可用).
这就是我得到的东西,就像responseText
在IE中一样,即使对于二进制数据也是如此.首先,将一些VBScript作为一次性注入,如下所示:
if(/msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent)) { var IEBinaryToArray_ByteStr_Script = "\r\n"+ "\r\n"; // inject VBScript document.write(IEBinaryToArray_ByteStr_Script); }
我正在使用的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); }
的转换码被提供Miskun.
非常快,效果很好.
我使用这种方法从Javascript读取和提取zip文件,也在一个用Javascript读取和显示EPUB文件的类中.表现非常合理.500kb文件大约半秒钟.
XMLHttpRequest.responseBody
是VBArray
包含原始字节的对象.您可以使用以下toArray()
函数将这些对象转换为标准数组:
var data = xhr.responseBody.toArray();