如何读取文件内容?Android读取PDF遇FileNotFoundException问题求助
嘿,这个问题我太熟了!你遇到的java.io.FileNotFoundException: content:/com.android.providers.downloads.documents/document/2295: open failed: ENOENT (No such file or directory),根源在于你把content URI直接当成了本地文件路径来用——这俩完全不是一回事儿!
为什么会报错?
content:/xxx这种格式的URI是Android内容提供者(Content Provider)用来标识资源的统一标识符,它指向的可能是沙箱内的文件、云端文件或者其他应用共享的资源,根本不是传统文件系统里的路径。直接用File类去打开它,系统自然找不到对应的文件。
正确的解决方法
别再纠结转成文件路径了,直接通过ContentResolver获取输入流来读取PDF内容才是正道,步骤如下:
1. 通过ContentResolver获取文件输入流
Android的ContentResolver提供了openInputStream()方法,能直接通过content URI拿到文件的输入流,之后你就可以用PDF解析库(比如iText、PDFBox)来读取内容了。
示例代码:
// 假设你已经从文件选择器拿到了content URI,比如data.getData() Uri pdfUri = ...; // 你的目标PDF的content URI ContentResolver resolver = getContentResolver(); try (InputStream inputStream = resolver.openInputStream(pdfUri)) { // 用PDFBox读取PDF文本的示例(需要先引入PDFBox依赖) PDDocument pdfDocument = PDDocument.load(inputStream); PDFTextStripper textStripper = new PDFTextStripper(); String pdfContent = textStripper.getText(pdfDocument); Log.d("PDF_CONTENT", "读取到的PDF内容:" + pdfContent); pdfDocument.close(); } catch (IOException e) { e.printStackTrace(); // 这里可以给用户弹个提示,比如"无法读取PDF文件" }
2. 确保权限到位
如果你的应用运行在Android 6.0(API 23)及以上,要注意权限问题:
- 如果你用的是文件选择器,推荐使用
Intent.ACTION_OPEN_DOCUMENT而不是旧的ACTION_GET_CONTENT,前者会授予持久的临时访问权限,后续不用重复申请。 - 若需要直接访问外部存储,别忘了申请
READ_EXTERNAL_STORAGE权限(Android 13及以上可以用READ_MEDIA_IMAGES/READ_MEDIA_VIDEO等细分权限,但PDF还是需要READ_EXTERNAL_STORAGE)。
启动文件选择器的示例代码(用AndroidX的ActivityResultLauncher,替代过时的startActivityForResult):
// 先注册文件选择器的回调 ActivityResultLauncher<Intent> pdfPickerLauncher = registerForActivityResult( new ActivityResultContracts.StartActivityForResult(), result -> { if (result.getResultCode() == Activity.RESULT_OK && result.getData() != null) { Uri pdfUri = result.getData().getData(); // 调用上面的读取逻辑处理这个uri } } ); // 启动文件选择器时调用 Intent pickPdfIntent = new Intent(Intent.ACTION_OPEN_DOCUMENT); pickPdfIntent.addCategory(Intent.CATEGORY_OPENABLE); pickPdfIntent.setType("application/pdf"); // 指定只选PDF文件 pdfPickerLauncher.launch(pickPdfIntent);
3. 别再尝试转本地路径(划重点!)
很多人会想把content URI转成/storage/emulated/0/xxx这种本地路径,但在Android 10(API 29)及以上的沙箱存储机制下,这种方法不仅不靠谱,还容易因为权限问题失败。直接用输入流处理才是符合Android设计规范的正确方式。
总结
核心就是一句话:不要把content URI当成本地文件路径,用ContentResolver的openInputStream()获取输入流,再配合PDF解析库读取内容,同时确保权限配置正确,问题就能解决啦!
内容的提问来源于stack exchange,提问作者VVB

