Android PDF编辑APP报错:open failed: ENOENT (文件或目录不存在)
Android PDF编辑器文件找不到问题修复
问题重现
开发一款可编辑PDF并保存到手机存储的Android应用,选择PDF文件后触发java.io.FileNotFoundException: coa 2.1.pdf: open failed: ENOENT (No such file or directory)错误,无法打开选中的PDF文件。
核心原因
- 路径解析完全错误:
getPathFromUri方法里,用MediaStore.Images.Media.DISPLAY_NAME拿到的只是文件名,不是完整文件路径;且Uri.getPath()对于content://类型的Uri(多数文件选择器返回的格式)并非真实文件路径,导致后续用FileInputStream找不到文件。 - 文件读取方式错误:直接通过文件名创建
FileInputStream不适用于content://类型的Uri,必须通过ContentResolver获取输入流。 - 输出路径无效:直接用文件名作为输出路径,会默认指向应用私有目录或无权限访问的位置,无法正确保存文件。
修复方案
1. 移除错误的路径解析逻辑,直接使用Uri
不需要再尝试将Uri转为文件路径,直接通过ContentResolver操作Uri即可。
2. 修改文件读取与保存逻辑
- 使用
getContentResolver().openInputStream(uri)获取PDF文件输入流。 - 将输出文件指定到外部公共存储的可访问目录(比如Download文件夹),确保应用有权限写入。
3. 添加权限配置
在AndroidManifest.xml中添加存储权限(针对不同Android版本做适配):
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" /> <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" android:minSdkVersion="30" />
Android 6.0+需要动态申请READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE权限;Android 11+可选择使用MANAGE_EXTERNAL_STORAGE或通过MediaStore保存文件。
修改后的完整代码
MainActivity.java
package com.example.pdfeditor; import android.Manifest; import android.content.Intent; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Bundle; import android.os.Environment; import android.view.View; import android.widget.Button; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import com.itextpdf.text.Document; import com.itextpdf.text.DocumentException; import com.itextpdf.text.Font; import com.itextpdf.text.Paragraph; import com.itextpdf.text.pdf.BaseFont; import com.itextpdf.text.pdf.PdfWriter; import java.io.InputStream; import java.io.OutputStream; import java.io.File; import java.io.FileOutputStream; public class MainActivity extends AppCompatActivity { private static final int REQUEST_CODE_PICK_PDF = 1; private static final int REQUEST_CODE_STORAGE_PERMISSION = 2; private Uri selectedPdfUri; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button pickPdfButton = findViewById(R.id.btn); pickPdfButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 先检查存储权限 if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_CODE_STORAGE_PERMISSION); } else { openFilePicker(); } } }); } private void openFilePicker() { Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("application/pdf"); startActivityForResult(intent, REQUEST_CODE_PICK_PDF); } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == REQUEST_CODE_STORAGE_PERMISSION) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { openFilePicker(); } else { Toast.makeText(this, "需要存储权限才能选择PDF文件", Toast.LENGTH_SHORT).show(); } } } @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_CODE_PICK_PDF && resultCode == RESULT_OK && data != null) { selectedPdfUri = data.getData(); if (selectedPdfUri != null) { editAndSavePdf(); } } } private void editAndSavePdf() { try { // 获取PDF输入流 InputStream inputStream = getContentResolver().openInputStream(selectedPdfUri); // 创建输出文件:Download目录下的编辑后PDF File downloadDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS); String fileName = "edited_" + System.currentTimeMillis() + ".pdf"; File outputFile = new File(downloadDir, fileName); OutputStream outputStream = new FileOutputStream(outputFile); // 创建PDF文档并编辑 Document document = new Document(); PdfWriter writer = PdfWriter.getInstance(document, outputStream); document.open(); // 添加自定义文本(这里可以替换为读取原PDF内容并编辑的逻辑) Font font = new Font(BaseFont.createFont("assets/fonts/arial.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED), 12); Paragraph p = new Paragraph("这是编辑后的PDF内容", font); document.add(p); document.close(); writer.close(); inputStream.close(); outputStream.close(); Toast.makeText(this, "PDF已保存至:" + outputFile.getAbsolutePath(), Toast.LENGTH_LONG).show(); } catch (Exception e) { e.printStackTrace(); Toast.makeText(this, "错误:" + e.getMessage(), Toast.LENGTH_LONG).show(); } } }
说明:
- 新增了权限检查与动态申请逻辑,确保应用能访问存储。
- 直接使用
ContentResolver.openInputStream读取选中的PDF,避免路径解析错误。 - 输出文件指定到系统Download目录,确保文件可被用户找到且有权限写入。
- 如果需要编辑原PDF的内容而非新建文档,需要使用iText的
PdfReader读取原PDF内容,再通过PdfStamper进行编辑,示例中仅做了新建文档的演示。
错误日志(中文翻译)
W/System.err: java.io.FileNotFoundException: coa 2.1.pdf: 打开失败: ENOENT (没有该文件或目录) W/System.err: at libcore.io.IoBridge.open(IoBridge.java:574) W/System.err: at java.io.FileInputStream.<init>(FileInputStream.java:160) W/System.err: at java.io.FileInputStream.<init>(FileInputStream.java:115) W/System.err: at com.example.pdfeditor.MainActivity.openPdfFile(MainActivity.java:76) W/System.err: at com.example.pdfeditor.MainActivity.onActivityResult(MainActivity.java:54) W/System.err: at android.app.Activity.dispatchActivityResult(Activity.java:8951) W/System.err: at android.app.ActivityThread.deliverResults(ActivityThread.java:5987) W/System.err: at android.app.ActivityThread.handleSendResult(ActivityThread.java:6033) W/System.err: at android.app.servertransaction.ActivityResultItem.execute(ActivityResultItem.java:67) W/System.err: at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:45) W/System.err: at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) W/System.err: at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) W/System.err: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2574) W/System.err: at android.os.Handler.dispatchMessage(Handler.java:106) W/System.err: at android.os.Looper.loopOnce(Looper.java:226) W/System.err: at android.os.Looper.loop(Looper.java:313) W/System.err: at android.app.ActivityThread.main(ActivityThread.java:8757) W/System.err: at java.lang.reflect.Method.invoke(Native Method) W/System.err: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571) W/System.err: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1067) W/System.err: Caused by: android.system.ErrnoException: open failed: ENOENT (没有该文件或目录) W/System.err: at libcore.io.Linux.open(Native Method) W/System.err: at libcore.io.ForwardingOs.open(ForwardingOs.java:563) W/System.err: at libcore.io.BlockGuardOs.open(BlockGuardOs.java:274) W/System.err: at libcore.io.ForwardingOs.open(ForwardingOs.java:563) W/System.err: at android.app.ActivityThread$AndroidOs.open(ActivityThread.java:8619) W/System.err: at libcore.io.IoBridge.open(IoBridge.java:560) W/System.err: ... 19 more
内容的提问来源于stack exchange,提问作者THE Anonymous
相关产品推荐
相关产品推荐

