WebView 里 blob: URL 被拦截——扫码下载、PDF 预览为什么突然不行
问题场景
App 内嵌网页里点击"下载附件" → 网页代码用 URL.createObjectURL(blob) 生成一个 blob:https://xxx/xxx 格式的下载链接 → 点击后什么都不发生。
典型案例:
- 游戏内商店的 PDF 协议无法下载
- 二维码扫描结果页面无法保存图片
- 第三方客服(七陌、环信等)的文件下载失败
- PDF.js 渲染的 PDF 无法打印 / 下载
为什么发生
WebView 里 blob: 格式的下载链接触发时,需要走 WebChromeClient 的 onShowFileChooser 回调。但部分厂商在 WebView 层面默认禁用了 blob: 协议——下载事件根本到不了 onShowFileChooser。
| 厂商 | 默认 blob 策略 | 可解吗 |
|---|---|---|
| 小米 HyperOS | 默认拦截 | 应用层手动放开 |
| 华为 ArkWeb | 默认拦截 | 鸿蒙 API 层配置 |
| Google Android System WebView | 默认允许 | —— |
| OPPO / vivo / Samsung | 默认允许 | —— |
小米 HyperOS 的解法
需要在 WebView 初始化时手动开启 blob 协议:
WebSettings settings = webView.getSettings();
settings.setAllowContentAccess(true); // 必须开
// 小米 HyperOS 特有:放开 blob: 协议
try {
Class<?> clazz = Class.forName("com.android.webview.Settings");
Method m = clazz.getMethod("setAllowBlobUrls", boolean.class);
m.invoke(settings, true);
} catch (Exception e) {
// 非 HyperOS 设备走默认行为
}
这个 API 是小米私有的(非 Android 标准 API),需要反射调用,而且版本变化可能失效。
华为 ArkWeb 的解法
鸿蒙上需要用 ArkWeb 专有 API:
// JS 端显式配置下载
const link = document.createElement('a');
link.href = blobUrl;
link.download = filename;
link.click();
// 如果还是不行,尝试手动触发下载事件
link.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
鸿蒙 WebView 默认拦截非 HTTPS 来源的 blob 下载,尝试让后端改成 HTTPS 响应再试。
更通用的解法
与其依赖 blob 下载,不如服务端直接提供可下载的 URL(HTTPS),让 WebView 用标准下载流程:
// App 端拦截下载,调用系统下载管理器
webView.setDownloadListener(new DownloadListener() {
@Override
public void onDownloadStart(String url, String u, String m, String c, long l) {
// 交给 DownloadManager 或自己处理
}
});
这样绕过了 blob 协议安全限制,所有厂商的 WebView 都适用。