在 QML 中下载图像并将其转换为数据 URI
Download and Convert Image to Data URI in QML
我正在开发一个使用 QML、Qt 5.11.2 和 ESRI ArcGIS AppStudio 3.2 框架构建的应用程序。我需要下载一些 jpeg 图像以供离线查看,然后如果用户稍后选择关联的记录,则能够在图像元素中显示它们。我的首选方法是在 JavaScript 中触发 XMLHttpRequest,将响应转换为数据 URI,将其存储在我们的 SQLite 数据库中,然后在需要时将数据 URI 分配给图像源。除了将 XMLHttpRequest 响应转换为数据 URI 之外,此过程的每个步骤都有效。我有以下 JS 代码可以在浏览器中执行此操作:
var url = 'https://firebasestorage.googleapis.com/v0/b/christophereby-3733b.appspot.com/o/icon1.png?alt=media';
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.onreadystatechange = function() {
if (xhr.readyState === XMLHttpRequest.DONE) {
if (xhr.status === 200) {
var response = xhr.responseText;
var binary = "";
for (var i = 0; i < response.length; i++) {
binary += String.fromCharCode(response.charCodeAt(i) & 0xff);
}
var image = 'data:image/jpeg;base64,' + btoa(binary);
document.getElementById('img').src = image;
}
}
}
xhr.overrideMimeType('text/plain; charset=x-user-defined');
xhr.send();
<img src="#" id="img" />
但是,XMLHttpRequest 的 Qt 实现不支持 overrideMimeType 方法,而且浏览器和 Qt 实现之间的响应编码似乎不同。例如,response.charCodeAt(i) 方法 returns 第一个字符在浏览器中为 63369,在 Qt 中为 65533。 Qt 5.11 不支持其他 JS 方法(设置 XHR 响应类型、使用 Blob 对象等)。
以下是 QML 中不起作用的代码示例:
import QtQuick 2.7
import ArcGIS.AppFramework 1.0
App {
id: app
width: 400
height: 640
Component.onCompleted: {
var url = 'https://firebasestorage.googleapis.com/v0/b/christophereby-3733b.appspot.com/o/icon1.png?alt=media';
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.onreadystatechange = function() {
if (xhr.readyState === XMLHttpRequest.DONE) {
if (xhr.status === 200) {
var response = xhr.responseText;
var binary = "";
for (var i = 0; i < response.length; i++) {
binary += String.fromCharCode(response.charCodeAt(i) & 0xff);
}
var image = 'data:image/jpeg;base64,' + Qt.btoa(binary);
img.source = image;
}
}
}
xhr.send();
}
Image {
id: img
}
}
我也尝试过使用 ArcGIS.AppFramework NetworkRequest QML 对象来下载图像。我可以使用以下代码将图像保存到文件系统:
NetworkRequest {
url: 'https://firebasestorage.googleapis.com/v0/b/christophereby-3733b.appspot.com/o/icon1.png?alt=media'
responsePath: "~/ArcGIS/%1".arg('test.jpeg')
responseType: "blob"
onReadyStateChanged: {
if(readyState === NetworkRequest.DONE) {
console.log(response); //This is undefined
}
}
}
但是,我想在内存中执行此操作,并且当我将 responseType 设置为 blob 时,使用这种方法无法从 onReadyStateChanged 方法访问响应对象。将 responseType 设置为文本会导致与 XHR 请求相同的问题。
我找到了 this SO answer,但我首先尝试的选项 3 的替代方案涉及编写 C++ 代码,我不想这样做,以保持与 ArcGIS AppStudio 的最大兼容性。我在上面的代码中是否做错了什么,或者我可以采用另一种方法吗?
未实现 XMLHttpRequest overrideMimeType 方法的 Qt bug report(eyllanesc 的评论链接指向我)表明对数组缓冲区有一些支持。基于此并查看 QQmlXMLHttpRequest 源代码,我能够使它正常工作。这是一个示例(我有一个包含我自己的 Base64 编码器,因为 Qt.btoa 一个不起作用):
import QtQuick 2.7
import ArcGIS.AppFramework 1.0
App {
id: app
width: 400
height: 640
Component.onCompleted: {
var url = 'https://firebasestorage.googleapis.com/v0/b/christophereby-3733b.appspot.com/o/icon1.png?alt=media';
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'arraybuffer';
xhr.onreadystatechange = function() {
if (xhr.readyState === XMLHttpRequest.DONE) {
if (xhr.status === 200) {
var response = new Uint8Array(xhr.response);
var raw = "";
for (var i = 0; i < response.byteLength; i++) {
raw += String.fromCharCode(response[i]);
}
//FROM https://cdnjs.cloudflare.com/ajax/libs/Base64/1.0.1/base64.js
function base64Encode (input) {
var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
var str = String(input);
for (
// initialize result and counter
var block, charCode, idx = 0, map = chars, output = '';
str.charAt(idx | 0) || (map = '=', idx % 1);
output += map.charAt(63 & block >> 8 - idx % 1 * 8)
) {
charCode = str.charCodeAt(idx += 3/4);
if (charCode > 0xFF) {
throw new Error("Base64 encoding failed: The string to be encoded contains characters outside of the Latin1 range.");
}
block = block << 8 | charCode;
}
return output;
}
var image = 'data:image/png;base64,' + base64Encode(raw);
img.source = image;
}
}
}
xhr.send();
}
Image {
id: img
}
}
我正在开发一个使用 QML、Qt 5.11.2 和 ESRI ArcGIS AppStudio 3.2 框架构建的应用程序。我需要下载一些 jpeg 图像以供离线查看,然后如果用户稍后选择关联的记录,则能够在图像元素中显示它们。我的首选方法是在 JavaScript 中触发 XMLHttpRequest,将响应转换为数据 URI,将其存储在我们的 SQLite 数据库中,然后在需要时将数据 URI 分配给图像源。除了将 XMLHttpRequest 响应转换为数据 URI 之外,此过程的每个步骤都有效。我有以下 JS 代码可以在浏览器中执行此操作:
var url = 'https://firebasestorage.googleapis.com/v0/b/christophereby-3733b.appspot.com/o/icon1.png?alt=media';
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.onreadystatechange = function() {
if (xhr.readyState === XMLHttpRequest.DONE) {
if (xhr.status === 200) {
var response = xhr.responseText;
var binary = "";
for (var i = 0; i < response.length; i++) {
binary += String.fromCharCode(response.charCodeAt(i) & 0xff);
}
var image = 'data:image/jpeg;base64,' + btoa(binary);
document.getElementById('img').src = image;
}
}
}
xhr.overrideMimeType('text/plain; charset=x-user-defined');
xhr.send();
<img src="#" id="img" />
但是,XMLHttpRequest 的 Qt 实现不支持 overrideMimeType 方法,而且浏览器和 Qt 实现之间的响应编码似乎不同。例如,response.charCodeAt(i) 方法 returns 第一个字符在浏览器中为 63369,在 Qt 中为 65533。 Qt 5.11 不支持其他 JS 方法(设置 XHR 响应类型、使用 Blob 对象等)。
以下是 QML 中不起作用的代码示例:
import QtQuick 2.7
import ArcGIS.AppFramework 1.0
App {
id: app
width: 400
height: 640
Component.onCompleted: {
var url = 'https://firebasestorage.googleapis.com/v0/b/christophereby-3733b.appspot.com/o/icon1.png?alt=media';
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.onreadystatechange = function() {
if (xhr.readyState === XMLHttpRequest.DONE) {
if (xhr.status === 200) {
var response = xhr.responseText;
var binary = "";
for (var i = 0; i < response.length; i++) {
binary += String.fromCharCode(response.charCodeAt(i) & 0xff);
}
var image = 'data:image/jpeg;base64,' + Qt.btoa(binary);
img.source = image;
}
}
}
xhr.send();
}
Image {
id: img
}
}
我也尝试过使用 ArcGIS.AppFramework NetworkRequest QML 对象来下载图像。我可以使用以下代码将图像保存到文件系统:
NetworkRequest {
url: 'https://firebasestorage.googleapis.com/v0/b/christophereby-3733b.appspot.com/o/icon1.png?alt=media'
responsePath: "~/ArcGIS/%1".arg('test.jpeg')
responseType: "blob"
onReadyStateChanged: {
if(readyState === NetworkRequest.DONE) {
console.log(response); //This is undefined
}
}
}
但是,我想在内存中执行此操作,并且当我将 responseType 设置为 blob 时,使用这种方法无法从 onReadyStateChanged 方法访问响应对象。将 responseType 设置为文本会导致与 XHR 请求相同的问题。
我找到了 this SO answer,但我首先尝试的选项 3 的替代方案涉及编写 C++ 代码,我不想这样做,以保持与 ArcGIS AppStudio 的最大兼容性。我在上面的代码中是否做错了什么,或者我可以采用另一种方法吗?
未实现 XMLHttpRequest overrideMimeType 方法的 Qt bug report(eyllanesc 的评论链接指向我)表明对数组缓冲区有一些支持。基于此并查看 QQmlXMLHttpRequest 源代码,我能够使它正常工作。这是一个示例(我有一个包含我自己的 Base64 编码器,因为 Qt.btoa 一个不起作用):
import QtQuick 2.7
import ArcGIS.AppFramework 1.0
App {
id: app
width: 400
height: 640
Component.onCompleted: {
var url = 'https://firebasestorage.googleapis.com/v0/b/christophereby-3733b.appspot.com/o/icon1.png?alt=media';
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'arraybuffer';
xhr.onreadystatechange = function() {
if (xhr.readyState === XMLHttpRequest.DONE) {
if (xhr.status === 200) {
var response = new Uint8Array(xhr.response);
var raw = "";
for (var i = 0; i < response.byteLength; i++) {
raw += String.fromCharCode(response[i]);
}
//FROM https://cdnjs.cloudflare.com/ajax/libs/Base64/1.0.1/base64.js
function base64Encode (input) {
var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
var str = String(input);
for (
// initialize result and counter
var block, charCode, idx = 0, map = chars, output = '';
str.charAt(idx | 0) || (map = '=', idx % 1);
output += map.charAt(63 & block >> 8 - idx % 1 * 8)
) {
charCode = str.charCodeAt(idx += 3/4);
if (charCode > 0xFF) {
throw new Error("Base64 encoding failed: The string to be encoded contains characters outside of the Latin1 range.");
}
block = block << 8 | charCode;
}
return output;
}
var image = 'data:image/png;base64,' + base64Encode(raw);
img.source = image;
}
}
}
xhr.send();
}
Image {
id: img
}
}