使用XMLHttpRequest下载二进制数据,不使用overrideMimeType

8

我正在尝试使用XMLHttpRequest在Javascript中检索图像数据。

var xhr = new XMLHttpRequest();
xhr.open("GET", "http://www.celticfc.net/images/doc/celticcrest.png");
xhr.onreadystatechange = function() {
    if (xhr.readyState === 4) {
        var resp = xhr.responseText;
        console.log(resp.charCodeAt(0) & 0xff);
    }
};
xhr.send();

这些数据的第一个字节应该是0x89,但是任何高位字节都会返回0xfffd0xfffd & 0xff0xfd)。
这样的问题提供了使用overrideMimeType()函数的解决方案,但是我正在使用的平台(Qt/QML)不支持此功能。
我该如何正确下载数据?

1
你尝试过使用base64编码吗? - kbtz
似乎这是不可能的,我将使用Qt/C++本地下载它。 - funkybro
2
@funkybro。String.charCodeAt返回Unicode字符代码,而不是字节。这就是为什么xhr.responseText.charCodeAt(0)返回0xfff9。_xhr.responseText.charAt(1)==="P"_和_xhr.responseText.charAt(2)==="N"_和_xhr.responseText.charAt(2)==="G"_和_xhr.responseText.substr(1,3)==="PNG"_。 - Andrew D.
是的,那解释了我所看到的。我想使用 XHR 没有可移植的方式来下载二进制数据。 - funkybro
3个回答

8

Google I/O 2011: Web开发者的HTML5展示:惊叹和操作

获取二进制文件:新热点

var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://www.celticfc.net/images/doc/celticcrest.png', true);

xhr.responseType = 'arraybuffer';

xhr.onload = function(e) {
   if (this.status == 200) {
       var uInt8Array = new Uint8Array(this.response); // Note:not xhr.responseText

       for (var i = 0, len = uInt8Array.length; i < len; ++i) {
           uInt8Array[i] = this.response[i];
       }

       var byte3 = uInt8Array[4]; // byte at offset 4
   }
}

xhr.send();

我很乐意为您提供一杯免费啤酒,以感谢您发布了那个链接。 - Rishul Matta
1
@yms 的评论已经过时了,在Chromium 97.0.4692.45上使用arraybuffer响应类型确实会看到onprogress事件。 - davm85

0

我不熟悉Qt,但我在他们的文档中找到了这个。

string Qt::btoa ( data )
Binary to ASCII - this function returns a base64 encoding of data.

所以,如果你的图片是png格式,你可以尝试:

resp = "data:image/png;base64," + btoa(resp);
document.write("<img src=\""+resp+"\">");

如果一开始无法通过字符串访问正确的字节值,我不确定那将如何运作。 - funkybro
从0开始!布局引擎总是忽略与呈现图像无关的内容。 - kbtz

0

Qt版本5.13.1(支持qml环境下的本地Promise),纯qml。

  1. 函数fetch,类似于node-fetch
  2. 函数hexdump,将数据以十六进制形式转储,只需使用linux命令hexdump进行检查。
function createResponse(xhr) {
    let res = {};
    function headersParser() {
        let headersRaw = {};
        let lowerCaseHeaders = {};
        let rawHeaderArray = xhr.getAllResponseHeaders().split("\n");
        for(let i in rawHeaderArray) {
            let rawHeader = rawHeaderArray[i];
            let headerItem = rawHeader.split(":");
            let name = headerItem[0].trim();
            let value = headerItem[1].trim();
            let lowerName = name.toLowerCase();
            headersRaw[name] = value;
            lowerCaseHeaders [lowerName] = value;
        }
        return {
            "headersRaw": headersRaw,
            "lowerCaseHeaders": lowerCaseHeaders
        };
    }
    res.headers = {
        __alreayParse : false,
        raw: function() {
            if (!res.headers.__alreayParse) {
                let {headersRaw, lowerCaseHeaders} = headersParser();
                res.headers.__alreayParse = true;
                res.headers.__headersRaw = headersRaw;
                res.headers.__lowerCaseHeaders = lowerCaseHeaders;
            }
            return res.headers.__headersRaw;
        },
        get: function(headerName) {
            if (!res.headers.__alreayParse) {
                let {headersRaw, lowerCaseHeaders} = headersParser();
                res.headers.__alreayParse = true;
                res.headers.__headersRaw = headersRaw;
                res.headers.__lowerCaseHeaders = lowerCaseHeaders;
            }
            return res.headers.__lowerCaseHeaders[headerName.toLowerCase()];
        }
    };
    res.json = function() {
        if(res.__json) {
            return res.__json;
        }
        return res.__json = JSON.parse(xhr.responseText);
    }
    res.text = function() {
        if (res.__text) {
            return res.__text;
        }
        return res.__text = xhr.responseText;
    }
    res.arrayBuffer = function() {
        if (res.__arrayBuffer) {
            return res.__arrayBuffer;
        }
        return res.__arrayBuffer = new Uint8Array(xhr.response);
    }
    res.ok = (xhr.status >= 200 && xhr.status < 300);
    res.status = xhr.status;
    res.statusText = xhr.statusText;
    return res;
}
function fetch(url, options) {
    return new Promise(function(resolve, reject) {
        let requestUrl = "";
        let method = "";
        let headers = {};
        let body;
        let timeout;
        if (typeof url === 'string') {
            requestUrl = url;
            method = "GET";
            if (options) {
                requestUrl = options['url'];
                method = options['method'];
                headers = options['headers'];
                body = options['body'];
                timeout = options['timeout'];
            }
        } else {
            let optionsObj = url;
            requestUrl = optionsObj['url'];
            method = optionsObj['method'];
            headers = optionsObj['headers'];
            body = optionsObj['body'];
            timeout = optionsObj['timeout'];
        }
        let xhr = new XMLHttpRequest;
        if (timeout) {
            xhr.timeout = timeout;
        }
        // must set responseType to arraybuffer, then the xhr.response type will be ArrayBuffer
        // but responseType not effect the responseText
        xhr.responseType = 'arraybuffer';
        xhr.onreadystatechange = function() {
            // readyState as follow: UNSENT, OPENED, HEADERS_RECEIVED, LOADING, DONE
            if(xhr.readyState === XMLHttpRequest.DONE) {
                resolve(createResponse(xhr));
            }
        };
        xhr.open(method, requestUrl);
        // todo check headers
        for(var iter in headers) {
            xhr.setRequestHeader(iter, headers[iter]);
        }
        if("GET" === method || "HEAD" === method) {
            xhr.send();
        } else {
            xhr.send(body);
        }
    });
}
function hexdump(uint8array) {
    let count = 0;
    let line = "";
    let lineCount = 0;
    let content = "";
    for(let i=0; i<uint8array.byteLength; i++) {
        let c = uint8array[i];
        let hex =  c.toString(16).padStart (2, "0");
        line += hex + " ";
        count++;
        if (count === 16) {
            let lineCountHex = (lineCount).toString (16).padStart (7, "0") + "0";
            content += lineCountHex + " " + line + "\n";
            line = "";
            count = 0;
            lineCount++;
        }
    }
    if(line) {
        let lineCountHex = (lineCount).toString (16).padStart (7, "0") + "0";
        content += lineCountHex + " " + line + "\n";
        line = "";
        //            count = 0;
        lineCount++;
    }
    content+= (lineCount).toString (16).padStart (7, "0") + count.toString(16) +"\n";
    return content;
}
fetch("https://avatars2.githubusercontent.com/u/6630355")
.then((res)=>{
  try {
      let headers = res.headers.raw();
      console.info(`headers:`, JSON.stringify(headers));
      let uint8array = res.arrayBuffer();
      let hex = hexdump(uint8array);
      console.info(hex)
  }catch(error) {
      console.trace(error);
  }
})
.catch((error)=>{

});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接