AndroidのWebViewから画像などダウンロードさせたい場合。
JavaScript単体でやるのは無理でした。
※ たとえば Ajax とか XMLHttpRequest とか・・・
WebView向けの特殊手順が必要なので、
ここでは WebViewでダウンロード処理のやり方 とか紹介!
少し面倒なので忘備録としてメモしておきます。
ここでは次の場面を想定します。
- WebView内で画像ダウンロード
- 保存先はDonwloadsディレクトリ
その場合の基本的なやり方は次の通り
このページの目次
1.まずJavaScript側で画像ダウンロード
始めはJS側で画像のダウンロード処理を実行
大雑把な手順はこういう感じです。
- 画像をbase64形式のURLに変換
- そのURLを持つダウンロードリンク作成
- 手動または任意でダウンロードさせる
具体的なやり方は ▼次の2記事▼ を参照
ただ、この2記事だけだと分かりにくいかもしれません。
一応PNG画像でのコード例的なものを載せておきます。
▼ PNG画像をJS側でダウンロードさせるコード例
1 2 3 4 5 6 7 8 |
/// 任意の方法でPNG画像をBase64化 var dataUrl = "data:image/png;base64,iVB..."; /// ダミーリンクを作り自動ダウンロード var fileName = 'dummy_name' var link = $('<a>', { href: dataUrl, download: fileName }); $('body').append(link); link[0].click(); link.remove(); |
この時点では WebView は一切関わっていません。
だからAndroid端末への画像ダウンロード不可です。
2.WebViewにダウンロードリスナーを登録する
ここからが肝心なところ。
AndroidのWebView側でのコードを実装していきます。
まずは ダウンロードリスナーの実装 から。
例えば次のようなコードが必要になるんです。
▼ Activity内の onCreate にて・・・
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
/// WebViewを取得 webView = findViewById(R.id.web_view); /// ダウンロードリスナー登録 webView.setDownloadListener(new DownloadListener() { @Override public void onDownloadStart( String url, String userAgent, String contentDisposition, String mimetype, long contentLength ) { String script = "var xhr = new XMLHttpRequest();" + "xhr.open('GET', '" + url +"', true);" + "xhr.responseType = 'blob';" + "xhr.onload = function(e) {" + " if (this.status == 200) {" + " var reader = new FileReader();" + " reader.readAsDataURL(this.response);" + " reader.onloadend = function() {" + " base64Str = reader.result;" + " Android.downloadBlob(base64Str)" + " }" + " }" + "};" + "xhr.send();"; webView.evaluateJavascript(script, null); } }); /// WebViewでのアクセスとかの権限設定 WebSettings settings = webView.getSettings(); settings.setJavaScriptEnabled(true); settings.setAllowFileAccess(true); settings.setAllowFileAccessFromFileURLs(true); settings.setAllowUniversalAccessFromFileURLs(true); settings.setDomStorageEnabled(true); /// JavascriptInterfaceのメソッド登録 webView.addJavascriptInterface(this, "Android"); |
こういう感じで DownloadListener を登録
このコード内で webView.evaluateJavascript(script, null); から実行してるJavaScriptコードは丸々コピペでOKです。基本的には変える必要はありません。
またJSコード内に Android.downloadBlob という謎のメソッドがありますが、これは次から定義していくので気にしないでください。その名の通り Blob にしたデータをダウンロードさせるAndroid側のメソッドです。
あと webView.addJavascriptInterface(this, "Android"); のようにJSインターフェースの登録も忘れずに。ここでは分かりやすく "Android" というインターフェース名にしました。
3.WebView側から端末へ画像ダウンロード
最後にWebView側から端末へダウンロード保存。
ここでは donwloadBlob というメソッド名ですね。
それをActivity内にこう定義しました。
▼ このようなコードを Activity 内に追加
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
/// ダウンロードファイルの拡張子 private String blobExtension = "png"; /// ダウンロードファイルのMIMEタイプ private String blobMimeType = "image/png"; /** * WebViewからのファイルダウンロードを行う * @param xhrResult Base64形式のURL */ @JavascriptInterface public void downloadBlob(String xhrResult) { // XHRのResult文字列から不要な箇所を取り除き、素のBase64文字列のみにする。 String base64 = xhrResult.replaceFirst("^data:.+;base64,", ""); // Base64デコード byte[] blobBytes = Base64.decode(base64, 0); /// 日付をもとにファイル名生成 DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss"); String fileName = dateFormat.format(new Date())+"."+blobExtension; // デコードされた内容をファイルに書き込む File file = new File(Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_DOWNLOADS), fileName); try { FileOutputStream fos = new FileOutputStream(file, false); Toast.makeText(this, "Saved to "+file.getAbsolutePath(), Toast.LENGTH_LONG).show(); fos.write(blobBytes); fos.flush(); } catch (FileNotFoundException e) { Log.e(TAG, e.getMessage()+""); } catch (IOException e) { Log.e(TAG, e.getMessage()+""); } } |
メソッド定義の前に @JavascriptInterface を付けるとJSインターフェースとなり、WebView内のJavaScriptコードからJavaメソッド呼び出せるようになるんですよね。
これで端末へのダウンロード保存は完了。
ここでは Donwloads ディレクトリへ保存しています。
4.ContentResolverも更新すればより親切!
これで基本的な手順は終わりなんですが・・・
1つ不便というか不親切なところが残ってます、
それはContentResolverが更新されないこと
更新されてないと次の点で不便です。
- 端末側がファイルを認識できない
- ファイルマネージャーでも表示されない
- あとサムネイルとかも表示されない
だから ContentResolver 更新まですると親切です。
そのコード例を載せておくと次の通り
▼ 先ほどの downloadBlob にこういうJavaコード追加
1 2 3 4 5 6 7 8 9 10 11 12 13 |
/// ContentResolver更新 String imgPath = file.getAbsolutePath(); String imgName = file.getName(); String imgTitle = imgName.substring(0, imgName.indexOf(".")); String mimeType = dlFileMimeType; ContentResolver cr = getContentResolver(); ContentValues values = new ContentValues(); values.put(MediaStore.Images.ImageColumns.DATA, imgPath); values.put(MediaStore.Images.ImageColumns.DISPLAY_NAME, imgName); values.put(MediaStore.Images.ImageColumns.TITLE, imgTitle); values.put(MediaStore.Images.ImageColumns.SIZE, file.length()); values.put(MediaStore.Images.ImageColumns.MIME_TYPE, mimeType); cr.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); |
こうしておくとファイルマネーにも画像が表示され、保存画像(ファイル)にすぐアクセスできます。そちらの方が親切なのは言うまでもありません。
WebView画像ダウンロード手順のまとめ
長くなったので手順だけまとめ!
以上、WebViewで画像などファイルダウンロードさせる手順でした。
もし誤りなどあればコメントでご指摘ください。ではでは(^^)/~~~