Android WebView 如何获取blob链接数据并下载到设备存储
安卓WebView下载Blob格式文件解决方案
问题1:如何从blob: URL获取对应数据
你现有的JavaScriptInterface方案思路是正确的,只需要修正硬编码适配多格式、补全WebView配置即可正常运行,现有代码存在以下可优化点:
- 硬编码请求头
Content-type为image/gif,仅支持GIF格式,无法适配PDF、PNG等其他类型文件 - 保存文件时仅移除了GIF格式的base64前缀,其他格式的base64数据无法正确解码
- 缺少WebView触发下载的拦截逻辑,无法主动捕获blob下载请求
完整实现步骤:
- 先配置WebView基础参数
// 开启JS支持 webView.getSettings().setJavaScriptEnabled(true); // 添加JS交互接口 DownloadBlobFileJSInterface jsInterface = new DownloadBlobFileJSInterface(this); webView.addJavascriptInterface(jsInterface, "Android"); // 拦截下载请求 webView.setDownloadListener((url, userAgent, contentDisposition, mimetype, contentLength) -> { if (url.startsWith("blob:")) { // 执行JS代码获取blob的base64数据 webView.evaluateJavascript(jsInterface.getBase64StringFromBlobUrl(url), null); } });
- 优化后的JSInterface代码,支持多格式文件下载
class DownloadBlobFileJSInterface { private Context mContext; private DownloadSuccessListener mDownloadSuccessListener; public DownloadBlobFileJSInterface(Context context) { this.mContext = context; } public String getBase64StringFromBlobUrl(String blobUrl) { if (blobUrl.startsWith("blob")) { return "javascript: var xhr = new XMLHttpRequest();" + "xhr.open('GET', '" + blobUrl + "', true);" + "xhr.responseType = 'blob';" + "xhr.onload = function(e) {" + " if (this.status == 200) {" + " var blobFile = this.response;" + " var reader = new FileReader();" + " reader.readAsDataURL(blobFile);" + " reader.onloadend = function() {" + " base64data = reader.result;" + " Android.getBase64FromBlobData(base64data);" + " }" + " }" + "};" + "xhr.send();"; } return "javascript: console.log('It is not a Blob URL');"; } public void setDownloadSuccessListener(DownloadSuccessListener listener) { mDownloadSuccessListener = listener; } @JavascriptInterface public void getBase64FromBlobData(String base64Data) { convertToFileAndProcess(base64Data); } private void convertToFileAndProcess(String base64) { // 解析base64前缀获取文件类型 String type = base64.substring(base64.indexOf(":") + 1, base64.indexOf(";")); String suffix = MimeTypeMap.getSingleton().getExtensionFromMimeType(type); if (suffix == null) { // 未识别到类型默认用bin suffix = "bin"; } ContextWrapper wrapper = new ContextWrapper(mContext); String fullPath = wrapper.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString(); File targetFile = new File(fullPath + "/File_" + System.currentTimeMillis() + "." + suffix); saveFileToPath(base64, targetFile); Toast.makeText(mContext, "下载完成,已保存到Download目录", Toast.LENGTH_SHORT).show(); if (mDownloadSuccessListener != null) { mDownloadSuccessListener.downloadSuccess(targetFile.getAbsolutePath()); } } private void saveFileToPath(String base64, File filePath) { try { // 去掉所有格式的base64前缀 String pureBase64 = base64.substring(base64.indexOf(",") + 1); byte[] fileBytes = Base64.decode(pureBase64, 0); FileOutputStream os = new FileOutputStream(filePath, false); os.write(fileBytes); os.flush(); os.close(); } catch (Exception e) { e.printStackTrace(); Toast.makeText(mContext, "下载失败:" + e.getMessage(), Toast.LENGTH_SHORT).show(); } } public interface DownloadSuccessListener { void downloadSuccess(String absolutePath); } }
注意事项:
- Android 9及以上版本如果访问HTTP站点,需要在AndroidManifest.xml的application节点添加
android:usesCleartextTraffic="true" - 混淆规则需要添加:
-keepclassmembers class * { @android.webkit.JavascriptInterface <methods>; } - 不需要额外申请存储权限,
getExternalFilesDir是应用专属目录,Android 10及以上无需动态申请权限
问题2:是否可以将blob数据传递给Chrome完成下载
不可以。blob URL是和当前WebView的渲染进程上下文绑定的,仅在当前应用的WebView内存中有效,传递给Chrome后,Chrome没有对应内存中的blob数据,无法识别该链接,因此不能通过跳转Chrome的方式完成下载。
内容的提问来源于stack exchange,提问作者Marsad
相关产品推荐
相关产品推荐

