как я получаю доступ XHR responseBody (для двоичных данных) из JavaScript в IE?

Вы не можете уменьшить видимость метода, унаследованного от

. Поэтому, если супер-метод общедоступен, вы не можете перейти к защищенному или частному

Этот вопрос уже охватывает этот вопрос: Невозможно уменьшить видимость метода, унаследованного от родителя

25
задан Cheeso 27 May 2011 в 19:26
поделиться

5 ответов

Да, ответ, который я придумал для чтения двоичных данных через XHR в IE, заключается в использовании 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 =
    "<!-- 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-м индексе. Вот как я это настроил:

// 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);
}

Код преобразования был предоставлен Мискуном.

Очень быстро, отлично работает.

Я использовал этот метод для чтения и извлечения zip-файлов из Javascript, а также в классе, который читает и отображает файлы EPUB в Javascript. Очень разумная производительность. Около полсекунды для файла 500 КБ.

14
ответ дан Knu 28 November 2019 в 21:37
поделиться

Я бы предложил два других (быстрых) варианта:

  1. Во-первых, вы можете использовать ADODB.Recordset для преобразования байтового массива в строку. Я предполагаю, что этот объект более распространен, чем ADODB.Stream, который часто отключается по соображениям безопасности. Этот параметр ОЧЕНЬ быстрый, менее чем 30 мс для файла размером 500 КБ.

  2. Во-вторых, если компонент Recordset недоступен, существует трюк для доступа к данным байтового массива из Javascript . Отправьте ваш xhr.responseBody в VBScript, передайте через любую строковую функцию VBScript, такую ​​как CStr (не занимает много времени), и верните ее в JS. Вы получите странную строку с байтами, соединенными в 16-битный юникод (в обратном порядке). Затем вы можете быстро преобразовать эту строку в пригодную для использования строку теста с помощью регулярного выражения с заменой на основе словаря. Принимает около для 500 кБ.

Для сравнения, побайтовое преобразование через циклы занимает несколько минут для этого же файла размером 500 КБ, так что это не составляет труда :) Ниже код, который я использовал, чтобы вставьте в свой заголовок. Затем вызовите функцию ieGetBytes с вашим xhr.responseBody.

<!--[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]-->
3
ответ дан Louis LC 28 November 2019 в 21:37
поделиться

Большое спасибо за это решение. функция BinaryToArray () в VbScript прекрасно работает для меня.

Между прочим, мне нужны двоичные данные для предоставления их апплету. (Не спрашивайте меня, почему Applets нельзя использовать для загрузки двоичных данных. Короче говоря ... странная аутентификация 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]);
    }

Затем байтовый массив может быть преобразован в байтовый поток ввода для дальнейшей обработки.

1
ответ дан rk2010 28 November 2019 в 21:37
поделиться

Я пытался скачать файл и подписать его, используя 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);
}
1
ответ дан Ashley Medway 28 November 2019 в 21:37
поделиться

Спасибо за этот пост.

Я нашел эту ссылку полезной:

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

Специально для этой части:

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

Я добавил это на свою страницу htm. Затем я вызываю эту функцию из моего JavaScript:

 responseText = BinaryToString(xhr.responseBody);

Работает на IE8, IE9, IE10, FF & amp; Хром.

1
ответ дан George G 28 November 2019 в 21:37
поделиться
Другие вопросы по тегам:

Похожие вопросы: