Android 11保存位图报FileNotFoundException(ENOENT)错误如何解决
Android 11保存图片到公共存储报ENOENT异常修复
问题复现
在Android 11环境下实现位图保存为图片文件并传递到其他Activity时,持续抛出如下异常:
java.io.FileNotFoundException: open failed: ENOENT (No such file or directory)
对应问题代码:
private void savePhoto() { resolver = getContentResolver(); ContentValues contentValues = new ContentValues(); contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, "Image_" + ".jpg"); contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg"); contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + getFilesDir() + File.separator + "TextPhoto"); imageUri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues); // outputMediaFile.createNewFile(); SaveSettings build = new SaveSettings.Builder().setClearViewsEnabled(true).setTransparencyEnabled(true).build(); if (ActivityCompat.checkSelfPermission(this, "android.permission.WRITE_EXTERNAL_STORAGE") == 0) { mPhotoEditor.saveAsFile(new File(imageUri.getPath()).getAbsolutePath(), build, new PhotoEditor.OnSaveListener() { public void onFailure(Exception exc) { } public void onSuccess(String str) { listPhoto.set(position, str); EditPhotoActivity editPhotoActivity = EditPhotoActivity.this; intent.putStringArrayListExtra("AFTER", listPhoto); editPhotoActivity.setResult(115, editPhotoActivity.intent); finish(); } }); } }
根因分析
RELATIVE_PATH配置非法:MediaStore的RELATIVE_PATH参数是外部公共存储根目录下的相对路径,代码中拼接了应用私有目录getFilesDir(),属于无效路径,Android 11分区存储规则下不会自动创建对应目录。- Uri使用逻辑错误:
resolver.insert()返回的是content://协议的MediaStore Uri,直接调用getPath()拿到的不是文件系统真实绝对路径,传入需要本地文件路径的saveAsFile方法无法定位到实际存储位置。 - 权限逻辑过时:Android 11(API 30)及以上版本中,
WRITE_EXTERNAL_STORAGE权限不再生效,通过MediaStore操作公共图片目录不需要申请该权限,原有权限判断可能阻塞正常逻辑执行。
修复方案
- 修正存储路径配置,移除非法的私有目录拼接:
ContentValues contentValues = new ContentValues(); // 文件名增加时间戳避免重名覆盖 contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, "Image_" + System.currentTimeMillis() + ".jpg"); contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg"); // 仅保留公共目录下的合法相对路径 contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + File.separator + "TextPhoto"); Uri imageUri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
- 调整图片保存逻辑,先存应用私有临时目录再写入MediaStore,避免直接用Uri转文件路径的错误写法,同时移除过时的权限判断:
SaveSettings build = new SaveSettings.Builder() .setClearViewsEnabled(true) .setTransparencyEnabled(true) .build(); // 创建应用私有缓存目录下的临时文件,不需要任何存储权限 File tempSaveFile = new File(getCacheDir(), "temp_photo_edit_" + System.currentTimeMillis() + ".jpg"); mPhotoEditor.saveAsFile(tempSaveFile.getAbsolutePath(), build, new PhotoEditor.OnSaveListener() { @Override public void onFailure(Exception exc) { exc.printStackTrace(); // 补充失败toast提示逻辑即可 } @Override public void onSuccess(String str) { try { // 将临时文件写入MediaStore对应的Uri位置 InputStream inputStream = new FileInputStream(tempSaveFile); OutputStream outputStream = getContentResolver().openOutputStream(imageUri); byte[] buffer = new byte[4096]; int readLen; while ((readLen = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, readLen); } inputStream.close(); outputStream.flush(); outputStream.close(); // 删除临时缓存文件避免占用存储空间 tempSaveFile.delete(); // 跨页面传递直接传Uri字符串即可,不要传转义后的文件路径 listPhoto.set(position, imageUri.toString()); intent.putStringArrayListExtra("AFTER", listPhoto); setResult(115, intent); finish(); } catch (Exception e) { e.printStackTrace(); } } });
注意:接收图片的页面拿到传递的Uri字符串后,要通过
getContentResolver().openInputStream(Uri.parse(uriStr))的方式读取图片内容,不要尝试转成File对象直接读取,否则会触发权限或文件不存在错误。
内容的提问来源于stack exchange,提问作者Priyanka JBCoder
相关产品推荐
相关产品推荐

