You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

安卓WebView加载本地文件触发FileUriExposedException的最优解决方案问询

问题根因分析

首先明确崩溃触发逻辑:你没有主动创建URI是因为WebView默认行为触发了Intent跳转:当你点击HTML中指向图片资源的file://链接时,WebView检测到自身无法直接处理该类资源的预览请求,会自动构造ACTION_VIEW类型的Intent携带file URI调用系统外部应用打开,安卓7.0及以上系统禁止应用对外暴露file URI,因此直接抛出FileUriExposedException崩溃。

现有方案风险评估

你当前通过重写shouldOverrideUrlLoading强制所有链接在WebView内部加载的方案,确实可以阻止WebView向外发送Intent,解决崩溃问题,风险分场景判断:

  • 若你的WebView仅加载你自身可控的、存放在指定目录的本地网页,完全不会加载外部不可信网页:该方案没有安全隐患,可正常使用。
  • 若你的WebView后续可能加载外部不可信网页:该方案存在两处明显隐患:
    1. 所有类型的链接都会被强制在WebView加载,包含tel、sms、mailto、APK下载链接等特殊schema,会导致功能异常,甚至被恶意链接诱导执行危险操作
    2. 你已开启JavaScriptEnabled,若不可信网页恶意构造跨源读取逻辑,结合你开启的文件访问权限,可能导致本地敏感文件被窃取。
最优解决方案

根据你的业务需求选择对应方案即可:

方案A:不需要外部应用打开资源,所有内容都在WebView展示(最优轻量方案)

在原有拦截逻辑基础上增加规则限制,只允许加载指定目录的本地file协议资源,其他链接按正常逻辑处理,代码如下:

// 先添加安全配置,禁止跨源文件访问,降低风险
mWebView.getSettings().setAllowFileAccessFromFileURLs(false);
mWebView.getSettings().setAllowUniversalAccessFromFileURLs(false);

// 优化后的WebViewClient逻辑
mWebView.setWebViewClient(new WebViewClient (){
    // 安卓7.0及以上调用该方法
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
        Uri uri = request.getUrl();
        // 只处理本地file协议链接
        if ("file".equals(uri.getScheme())) {
            String filePath = uri.getPath();
            // 校验路径是否属于你允许的目录,防止恶意读取其他文件
            if (filePath != null && filePath.startsWith("/storage/emulated/0/fotoshp/")) {
                view.loadUrl(uri.toString());
                return true;
            }
            // 不属于允许目录的链接直接拦截,不加载
            return true;
        }
        // 其他协议走默认逻辑,你也可以根据需求自定义处理
        return super.shouldOverrideUrlLoading(view, request);
    }

    // 兼容安卓7.0以下版本
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        Uri uri = Uri.parse(url);
        if ("file".equals(uri.getScheme())) {
            String filePath = uri.getPath();
            if (filePath != null && filePath.startsWith("/storage/emulated/0/fotoshp/")) {
                view.loadUrl(url);
                return true;
            }
            return true;
        }
        return super.shouldOverrideUrlLoading(view, url);
    }
});

方案B:需要允许用户点击图片后用系统相册打开

提前在AndroidManifest中配置好FileProvider,拦截到图片资源后将file URI转换为content URI再发送Intent,代码如下:

mWebView.setWebViewClient(new WebViewClient (){
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
        Uri uri = request.getUrl();
        if ("file".equals(uri.getScheme())) {
            String filePath = uri.getPath();
            if (filePath == null || !filePath.startsWith("/storage/emulated/0/fotoshp/")) {
                return true;
            }
            // 判断是否为图片资源
            if (filePath.endsWith(".jpg") || filePath.endsWith(".jpeg") || filePath.endsWith(".png") || filePath.endsWith(".webp")) {
                // 转换为content URI
                File imgFile = new File(filePath);
                Uri contentUri = FileProvider.getUriForFile(context, "你的应用包名.fileprovider", imgFile);
                Intent openIntent = new Intent(Intent.ACTION_VIEW);
                openIntent.setDataAndType(contentUri, "image/*");
                openIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                context.startActivity(openIntent);
                return true;
            }
            // 其他本地资源在WebView加载
            view.loadUrl(uri.toString());
            return true;
        }
        return super.shouldOverrideUrlLoading(view, request);
    }
});

内容的提问来源于stack exchange,提问作者Luis A. Florit

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 22:36:03