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
相关产品推荐
相关产品推荐

