Android读取CSV文件遇FileNotFoundException错误求助
排查java.io.FileNotFoundException: ENOENT (No such file or directory)错误
错误原因分析
- Android 10+存储权限限制:
Environment.getExternalStorageDirectory()在Android 10(API 29)及以上因Scoped Storage机制被限制,直接访问外部存储根目录下的自定义文件夹会触发路径访问失败。 - 权限请求逻辑错误:原代码用
&&判断权限,仅当两个权限都未授予时才请求,实际只要有一个权限缺失就需要申请;另外MANAGE_EXTERNAL_STORAGE是Android 11+的特殊权限,无法通过普通弹窗请求,需单独处理。 - 路径拼接不规范:用字符串拼接路径易出现斜杠重复/缺失等问题,且仅检查文件夹存在性,未验证目标CSV文件是否存在。
- 代码冗余与风险:循环内重复创建数据库实例会浪费资源,且未处理CSV行数据不完整导致的数组越界风险。
解决方案
一、适配Android 10+存储访问
根据需求选择以下两种方案:
方案1:使用应用专属外部存储目录(推荐)
此目录属于应用私有,无需申请额外权限(Android 10+),卸载应用时文件会被自动清理:
// 获取应用专属外部存储目录 File carpeta = new File(getExternalFilesDir(null), "BaseDeDatosAlumnos"); // 确保文件夹存在,不存在则创建 if (!carpeta.exists()) { carpeta.mkdirs(); } // 用File构造方法拼接文件路径,避免字符串错误 File archivo = new File(carpeta, "prueba.csv");
方案2:访问公共目录下的自定义文件夹(需特殊处理)
- Android 10(API 29):在
AndroidManifest.xml中启用旧存储模型:
<application ... android:requestLegacyExternalStorage="true"> </application>
- Android 11+(API 30):需引导用户手动开启全存储访问权限:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { if (!Environment.isExternalStorageManager()) { Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION); Uri uri = Uri.fromParts("package", getPackageName(), null); intent.setData(uri); startActivity(intent); } }
二、修复权限请求逻辑
修正权限判断条件,并区分普通权限与特殊权限的处理逻辑:
public void pedirPermisos() { int permisoEscritura = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE); int permisoLectura = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE); // 只要有一个权限未授予就请求(将&&改为||) if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) { if (permisoEscritura != PackageManager.PERMISSION_GRANTED || permisoLectura != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}, RecuestCode); } } // 单独处理Android 11+的全存储权限 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { if (!Environment.isExternalStorageManager()) { Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION); Uri uri = Uri.fromParts("package", getPackageName(), null); intent.setData(uri); startActivity(intent); } } }
三、完善文件存在性检查与路径处理
public void importarCSV() { // 替换为方案1或方案2的路径逻辑 File carpeta = new File(getExternalFilesDir(null), "BaseDeDatosAlumnos"); File archivo = new File(carpeta, "prueba.csv"); if (!carpeta.exists()) { Toast.makeText(ActividadPrincipalAdmin.this, "NO EXISTE LA CARPETA", Toast.LENGTH_SHORT).show(); } else if (!archivo.exists()) { Toast.makeText(ActividadPrincipalAdmin.this, "NO EXISTE EL ARCHIVO prueba.csv", Toast.LENGTH_SHORT).show(); } else { String cadena; try { FileReader fileReader = new FileReader(archivo); BufferedReader bufferedReader = new BufferedReader(fileReader); // 将数据库实例移到循环外,避免重复创建 DbHelper admin = new DbHelper(ActividadPrincipalAdmin.this); SQLiteDatabase db = admin.getWritableDatabase(); while ((cadena = bufferedReader.readLine()) != null) { String[] arreglo = cadena.split(","); // 检查CSV行数据是否完整,避免数组越界 if (arreglo.length < 6) { Toast.makeText(ActividadPrincipalAdmin.this, "CSV格式错误:行数据不完整", Toast.LENGTH_SHORT).show(); continue; } ContentValues registro = new ContentValues(); registro.put("nControl", arreglo[1]); registro.put("nombre", arreglo[2]); registro.put("carrera", arreglo[3]); registro.put("semestre", arreglo[4]); registro.put("genero", arreglo[5]); db.insert("t_alumnos", null, registro); } // 读取完成后统一提示,避免循环内重复弹窗 Toast.makeText(ActividadPrincipalAdmin.this, "SE IMPORTO CORRECTAMENTE", Toast.LENGTH_SHORT).show(); bufferedReader.close(); db.close(); } catch (FileNotFoundException e) { Toast.makeText(ActividadPrincipalAdmin.this, "ERROR: 未找到目标文件", Toast.LENGTH_LONG).show(); } catch (Exception e) { Toast.makeText(ActividadPrincipalAdmin.this, "ERROR: " + e.getMessage(), Toast.LENGTH_LONG).show(); Toast.makeText(ActividadPrincipalAdmin.this, "LO LAMENTO TIENES UN ERROR", Toast.LENGTH_SHORT).show(); } } }
内容的提问来源于stack exchange,提问作者Vicio028
相关产品推荐
相关产品推荐

