Android WebView下载PDF时触发“仅支持HTTP/HTTPS URI”异常的问题排查求助
Android WebView下载PDF时触发“仅支持HTTP/HTTPS URI”异常的问题排查求助
我来帮你分析下这个问题~核心原因其实很明确:Android WebView的默认下载处理器只认HTTP/HTTPS开头的URI,而你JS里用URL.createObjectURL(blob)生成的是blob://协议的链接,这就触发了那个IllegalArgumentException。浏览器(比如Chrome)本身支持处理Blob链接的下载,但WebView的安全限制更严格,默认不允许这种本地协议的下载请求。
下面给你几个可行的解决方案,你可以根据自己的需求选择:
方案一:自定义WebView下载监听,适配Blob下载(推荐)
这个方案需要Android原生代码和JS配合,让Native来处理Blob数据的保存:
Android端修改WebView配置:
给WebView设置自定义的DownloadListener,识别Blob协议并通过JS交互获取数据:webView.setDownloadListener(new DownloadListener() { @Override public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimeType, long contentLength) { if (url.startsWith("blob:")) { // 调用JS方法获取Blob的Base64数据 webView.evaluateJavascript("getBlobData('" + url + "')", new ValueCallback<String>() { @Override public void onReceiveValue(String base64Data) { if (base64Data == null || base64Data.isEmpty()) { Toast.makeText(getApplicationContext(), "获取下载数据失败", Toast.LENGTH_SHORT).show(); return; } // 解码Base64并保存为文件 try { // 去掉JS传递过来的引号(evaluateJavascript会自动加引号) String cleanData = base64Data.replace("\"", ""); byte[] blobBytes = Base64.decode(cleanData, Base64.DEFAULT); // 保存到应用下载目录 File downloadDir = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS); File pdfFile = new File(downloadDir, "Report Sheet.pdf"); FileOutputStream fos = new FileOutputStream(pdfFile); fos.write(blobBytes); fos.close(); // 提示用户下载完成 Toast.makeText(getApplicationContext(), "PDF已下载到" + pdfFile.getAbsolutePath(), Toast.LENGTH_LONG).show(); } catch (Exception e) { e.printStackTrace(); Toast.makeText(getApplicationContext(), "下载失败", Toast.LENGTH_SHORT).show(); } } }); } else { // 正常HTTP/HTTPS链接,用系统下载管理器处理 Intent downloadIntent = new Intent(Intent.ACTION_VIEW); downloadIntent.setData(Uri.parse(url)); startActivity(downloadIntent); } } });JS端添加辅助方法:
新增一个getBlobData方法,把Blob转成Base64格式传递给Native:function getBlobData(blobUrl) { return new Promise((resolve) => { fetch(blobUrl) .then(response => response.blob()) .then(blob => { const reader = new FileReader(); reader.onloadend = () => { // 去掉Base64前缀,只传递纯数据部分 const base64Str = reader.result.split(',')[1]; resolve(base64Str); }; reader.readAsDataURL(blob); }) .catch(err => { console.error("获取Blob数据失败:", err); resolve(null); }); }); }
方案二:直接让WebView处理原始HTTP下载
如果你不需要在JS里精细控制下载进度,可以修改JS逻辑,直接触发原始的HTTP下载请求,跳过Blob转换步骤:
function utils_teacher_progressDownload_get(url, apptype, modal, defaultfilename) { if (modal) { $('#' + modal).modal({backdrop: 'static', keyboard: false},'show'); } // 直接创建a标签指向原始URL,让WebView触发下载请求 const a = document.createElement("a"); a.href = url; a.download = defaultfilename; document.body.appendChild(a); a.click(); document.body.removeChild(a); // 这里需要Native配合,在下载完成/失败时通知JS关闭模态框 // 比如通过WebView的JavascriptInterface发送回调 }
这种方式的缺点是JS无法直接获取下载进度,需要Native在下载开始、完成、失败时通过JavascriptInterface通知JS更新模态框状态。
方案三:尝试开启WebView的Blob访问权限(兼容性有限)
你可以给WebView设置以下参数,尝试让它支持Blob协议的下载,但注意这个方案在Android 11及以上版本可能因为安全限制失效:
WebSettings webSettings = webView.getSettings(); webSettings.setAllowFileAccess(true); webSettings.setAllowFileAccessFromFileURLs(true); webSettings.setAllowUniversalAccessFromFileURLs(true);
最后给你两个调试小技巧:
- 在JS里
console.log(url),确认生成的链接确实是blob:开头的; - 在Android的
DownloadListener里打印url,验证触发异常的请求来源。
备注:内容来源于stack exchange,提问作者Kira
相关产品推荐
相关产品推荐

