You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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操作公共图片目录不需要申请该权限,原有权限判断可能阻塞正常逻辑执行。

修复方案

  1. 修正存储路径配置,移除非法的私有目录拼接:
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);
  1. 调整图片保存逻辑,先存应用私有临时目录再写入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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.31 12:15:39