Monaca
テクノロジー
ノウハウ・TIPS

WKWebView環境でダウンロードした画像を表示してみる

Cordova

Monaca

WKWebView

Monacaチームの小田川です。

これまでは、Qiitaに月一くらいのペースで投稿していましたが、今回からアシアルブログに投稿することになりました。今回は、WKWebView環境で外部サーバからダウンロードした画像を画面に表示するテストをしてみたいと思います。

サンプルイメージ

file:// スキーム

Monacaで利用しているCordovaでは、外部リソースにアクセスする際に、file:// スキームが使用されます。WKWebViewでは、セキュリティー制限により、file:// スキームでリソースにアクセスすることができません。

Monacaプロジェクトの www フォルダ内に配置されているリソースの場合は、Monacaから提供されている Custom Schemeプラグイン を使用することで対応することができますが、外部サーバからアプリ内にダウンロードした画像ファイルなどには、アクセスすることができません。

ダウンロードした画像を表示する

外部サーバからアプリ内にダウンロードした画像ファイルなどにアクセスする場合は、blob データを利用します。

ダウンロードする画像ファイルをblobデータとしてダウンロードした場合は、window.URL.createObjectURL から取得できるURLを使用することで、画像にアクセスすることができます。

<script>
  function downloadFile1() {
    var xmlHttpRequest = new XMLHttpRequest();

    xmlHttpRequest.open("GET", "https://cordova.apache.org/static/img/cordova_bot.png", true);
    xmlHttpRequest.responseType = "blob";

    xmlHttpRequest.onload = function(event) {
      var blob = xmlHttpRequest.response;
      if (blob) {
        // blobデータのURLを取得
        var url = window.URL.createObjectURL(blob);

        // 画像を表示
        document.getElementById('img1').src = url;
      } else {
        console.log('error');
      }
    };

    // ファイルをダウンロード
    xmlHttpRequest.send();
  }
</script>
<body>
  <input type="button" value="downloadFile1" onclick="downloadFile1()" />
  <img id="img1" />
</body>

blobデータとして画像を表示するため、Content-Security-Policyblob: を追加する必要があります。

<meta http-equiv="Content-Security-Policy" content="default-src * blob: data: gap: https://ssl.gstatic.com; style-src * 'unsafe-inline'; script-src * 'unsafe-inline' 'unsafe-eval'">

WKWebView用のカスタムビルドデバッガーで window.URL.createObjectURL を使用した場合は、下記のようなURLが取得されます。

blob:monaca-debugger://アプリID.monaca.io/99e8aba5-f365-4b3c-b17a-bac2ec802dab

デバッグビルドしたアプリで window.URL.createObjectURL を使用した場合は、下記のようなURLが取得されます。

blob:null/c1212065-cb51-4d73-911b-e1b2e9ccb559

WKWbViewでは、file:// スキームが null に変換されます。

上記のような null が設定されたURLでも画像が表示されますが、Custom Schemeプラグインを有効にした場合は、下記のようなURLが取得されます。

blob:monaca-app://monaca.io/c1212065-cb51-4d73-911b-e1b2e9ccb559

ダウンロードしてファイル保存した画像を表示する その1

ダウンロードしてファイル保存した画像の場合も、保存した画像ファイルをblobデータとして取得することで、window.URL.createObjectURL を利用することができます。

サンプルでは、Fileプラグイン を使用しています。
Fileプラグインの cordova.file.documentsDirectory から取得できるDocumentsフォルダパスにダウンロードしたファイルを保存しています。

<script>
  function downloadFile2() {
    // ファイル保存ディレクトリに cordova.file.documentsDirectory を設定
    window.resolveLocalFileSystemURL(cordova.file.documentsDirectory, function (dirEntry) {
      var fileName = "sample.png";
      var mimeType = "image/png"
      var xmlHttpRequest = new XMLHttpRequest();

      xmlHttpRequest.open("GET", "https://cordova.apache.org/static/img/cordova_bot.png", true);
      xmlHttpRequest.responseType = 'blob';

      xmlHttpRequest.onload = function() {
        if (this.status == 200) {
          var blob = new Blob([this.response], { type: mimeType });

          // ファイルの作成
          dirEntry.getFile(fileName, { create: true, exclusive: false }, function (fileEntry) {
            fileEntry.createWriter(function (fileWriter) {
              fileWriter.onwriteend = function() {
                if (blob.type == mimeType) {
                  fileEntry.file(function (file) {
                    var reader = new FileReader();

                    reader.onloadend = function() {
                      // 読み込んだデータから blob データを作成
                      var blob = new Blob([new Uint8Array(this.result)], { type: mimeType });

                      // ファイルパスの取得
                      // file:// スキームパスのため、アクセスできません。
                      // var url = fileEntry.toURL();

                      // blobデータのURLを取得
                      var url = window.URL.createObjectURL(blob);

                      console.log("fileEntry.toURL: " + fileEntry.toURL());
                      console.log("window.URL.createObjectURL: " + window.URL.createObjectURL(blob));

                      // 画像を表示
                      document.getElementById('img1').src = url;
                    };

                    // ファイルの読み込み
                    reader.readAsArrayBuffer(file);
                  });
                }
              };

              fileWriter.onerror = function(err) {
                console.log("Failed file write: " + err.toString());
              };

              // ファイルへ書き込み
              fileWriter.write(blob);
            });
          });
        }
      };

      // ファイルをダウンロード
      xmlHttpRequest.send();

    }, function(err) {
      console.log('error: resolveLocalFileSystemURL');
    }); 
  }
</script>
<body>
  <input type="button" value="downloadFile2" onclick="downloadFile2()" />
  <img id="img1" />
</body>

画像を表示するためのURLに fileEntry.toURL() を使用した場合は、file:// スキームパスになるため、画像は表示されません。

インスペクターで確認すると、下記のようなエラーが表示されます。

Not allowed to load local resource: file:///var/mobile/Containers/Data/Application/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/Documents/sample.png
Failed to load resource: The operation couldn’t be completed. Operation not permitted

ダウンロードしてファイル保存した画像を表示する その2

WKWebViewの file:// スキーム制限は、ファイルが保存されている場所に依存しています。

ダウンロードしてファイル保存した画像を表示する その1 では、ファイルの保存場所に cordova.file.documentsDirectory を指定しました。この cordova.file.documentsDirectory で取得できるパスは、アプリ内の Documents フォルダになります。Documentsフォルダの場合は、file:// スキーム制限が適用されます。

アプリ内の tmp フォルダ内のファイルの場合は、file:// スキーム制限は適用されません。そのため、tmpフォルダ内のファイルの場合は、file:// スキームのパスを使用することができます。tmpフォルダのパスは、cordova.file.tempDirectory で取得できます。

注意点として、Custom Schemeプラグインを有効にしている場合や、WKWebView用のカスタムビルドデバッガーを使用している場合は、仕様上、file:// スキーム制限が適用されるため、window.URL.createObjectURL を使用してください。

おわりに

WKWebView環境でダウンロードした画像などが表示されない場合は、一度、window.URL.createObjectURL で取得したURLやファイルの保存先を cordova.file.tempDirectory にして試してみてください。

author img for keiji_asial

keiji_asial

前の記事へ

次の記事へ

一覧へ戻る
PAGE TOP