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

Android端PDF查看遇FileUriExposedException异常,求解决

解决Android FileUriExposedException 问题

嘿,我一眼就发现问题根源啦!你遇到的FileUriExposedException,本质是把网络PDF的URL当成本地文件路径来处理了。

看报错信息里的路径:file:///storage/emulated/0/https%3A/www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf,这说明你的代码把网络链接https://www.w3.org/.../dummy.pdf直接拼到了外部存储目录下,试图创建一个本地File对象——但这个文件根本不存在于你的设备存储里,而且这种用法本身就不符合FileProvider的使用场景。

正确的处理步骤

你需要区分两种情况:如果是本地文件路径,直接用FileProvider打开;如果是网络PDF链接,必须先把文件下载到本地存储,再打开。这里显然是第二种情况,下面是修改方案:

1. 先添加必要权限(清单文件)

<!-- 网络权限,用于下载PDF -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- Android 10及以下需要的存储权限,11+建议用Scoped Storage -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="29" />

2. 修改点击事件代码

先实现一个简单的PDF下载逻辑(这里用OkHttp做示例,你也可以用其他网络库),下载完成后再用FileProvider打开:

holder.pdf.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        String pdfUrl = opportunity.getPdf_link();
        // 先判断是网络URL还是本地路径
        if (pdfUrl.startsWith("http://") || pdfUrl.startsWith("https://")) {
            // 网络PDF,先下载到本地
            downloadPdf(pdfUrl);
        } else {
            // 本地文件路径,检查文件是否存在后打开
            File file = new File(Environment.getExternalStorageDirectory(), pdfUrl);
            if (file.exists()) {
                openLocalPdf(file);
            } else {
                Toast.makeText(mContext, "文件不存在", Toast.LENGTH_SHORT).show();
            }
        }
    }

    // 下载PDF到本地的方法示例
    private void downloadPdf(String pdfUrl) {
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder().url(pdfUrl).build();
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                mContext.runOnUiThread(() -> 
                    Toast.makeText(mContext, "下载失败:" + e.getMessage(), Toast.LENGTH_SHORT).show()
                );
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (!response.isSuccessful()) {
                    mContext.runOnUiThread(() -> 
                        Toast.makeText(mContext, "请求失败", Toast.LENGTH_SHORT).show()
                    );
                    return;
                }
                // 保存到外部存储的Download目录
                File downloadDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
                String fileName = "temp_" + System.currentTimeMillis() + ".pdf";
                File pdfFile = new File(downloadDir, fileName);
                // 写入文件
                try (InputStream in = response.body().byteStream();
                     FileOutputStream out = new FileOutputStream(pdfFile)) {
                    byte[] buffer = new byte[4096];
                    int bytesRead;
                    while ((bytesRead = in.read(buffer)) != -1) {
                        out.write(buffer, 0, bytesRead);
                    }
                    // 下载完成后在主线程打开PDF
                    mContext.runOnUiThread(() -> openLocalPdf(pdfFile));
                } catch (IOException e) {
                    mContext.runOnUiThread(() -> 
                        Toast.makeText(mContext, "保存文件失败:" + e.getMessage(), Toast.LENGTH_SHORT).show()
                    );
                }
            }
        });
    }

    // 打开本地PDF的方法
    private void openLocalPdf(File file) {
        try {
            Uri pdfUri = FileProvider.getUriForFile(mContext, mContext.getPackageName() + ".provider", file);
            Intent pdfOpenIntent = new Intent(Intent.ACTION_VIEW);
            pdfOpenIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_ACTIVITY_CLEAR_TOP);
            pdfOpenIntent.setDataAndType(pdfUri, "application/pdf");
            // 确保有应用能打开PDF
            if (pdfOpenIntent.resolveActivity(mContext.getPackageManager()) != null) {
                mContext.startActivity(pdfOpenIntent);
            } else {
                Toast.makeText(mContext, "没有找到可以打开PDF的应用", Toast.LENGTH_SHORT).show();
            }
        } catch (Exception e) {
            Toast.makeText(mContext, "打开失败:" + e.getMessage(), Toast.LENGTH_SHORT).show();
            e.printStackTrace();
        }
    }
});

3. 注意事项

  • 如果你用OkHttp,记得在build.gradle里添加依赖:implementation 'com.squareup.okhttp3:okhttp:4.12.0'(版本可以根据需要调整)
  • Android 11及以上版本,建议使用Scoped Storage来处理文件,避免申请MANAGE_EXTERNAL_STORAGE权限
  • 下载文件时要处理网络异常、存储权限申请等情况(比如动态申请WRITE_EXTERNAL_STORAGE权限)

内容的提问来源于stack exchange,提问作者user13207116

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 10:12:43