下载图像并将其转换为QML中的数据URI

时间:2018-12-21 16:38:27

标签: javascript qt qml arcgis esri

我正在开发使用QML,Qt 5.11.2和ESRI ArcGIS AppStudio 3.2框架构建的应用程序。我需要下载一些jpeg图像以供脱机查看,然后如果用户以后选择关联的记录,则能够在图像元素中显示它们。我的首选方法是在JavaScript中触发XMLHttpRequest,将响应转换为数据URI,将其存储在我们的SQLite数据库中,然后在需要时将数据URI分配给Image源。除了将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)方法对于浏览器中的第一个字符返回63369,在Qt中返回65533。 Qt 5.11不支持其他JS方法(设置XHR responseType,使用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设置为text会导致出现与XHR请求相同的问题。

我找到了this SO answer,但是我尝试过的选项3的替代方案都涉及编写C ++代码,为了保持与ArcGIS AppStudio的最大兼容性,我不想这样做。上面的代码中我在做错什么吗?还是可以采取另一种方法?

1 个答案:

答案 0 :(得分:1)

未实现的XMLHttpRequest overlayMimeType方法的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
    }
}