You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Android项目相机/图库图片保存路径获取失败,报文件目录不存在异常

Hey,我之前也踩过这个坑!咱们来一步步拆解问题,解决这个创建文件失败的问题。

首先看你报错的No such file or directory,大概率是这几个原因:


1. 路径拼接错误

你用Environment.getExternalStorageDirectory() + IMAGE_DIRECTORY拼接路径,如果IMAGE_DIRECTORY的定义没有以/开头(比如写的是"MyImages"而不是"/MyImages"),那最终路径会变成/storage/emulated/0MyImages——这显然是个不存在的目录,mkdirs()自然创建失败,后续createNewFile()肯定报错。

2. Android 10+的Scoped Storage限制

从Android 10(API 29)开始,系统引入了Scoped Storage,禁止直接访问外部存储的根目录和公共目录(比如DCIM、Pictures)。你用Environment.getExternalStorageDirectory()直接创建目录,在Android 10+上会被系统限制,导致mkdirs()执行失败。

3. 没检查mkdirs()的返回值

你的代码里只调用了wallpaperDirectory.mkdirs();,但没判断它是否创建成功。如果因为权限、系统限制等原因创建目录失败,后续操作必然报错,你却没有提前处理这个情况。


解决方案:适配Scoped Storage的正确写法

优先推荐使用应用专属外部存储目录,这个目录不需要额外权限(Android 10+),卸载应用时会自动清理,完全符合系统规范。修改后的代码如下:

public String saveImage(Bitmap myBitmap) {
    ByteArrayOutputStream bytes = new ByteArrayOutputStream();
    myBitmap.compress(Bitmap.CompressFormat.JPEG, 90, bytes);

    // 使用应用专属的图片存储目录,适配Android 10+
    File wallpaperDirectory = new File(getExternalFilesDir(Environment.DIRECTORY_PICTURES), "MyAppImages");
    
    // 检查目录,创建失败则打日志返回
    if (!wallpaperDirectory.exists()) {
        boolean isDirCreated = wallpaperDirectory.mkdirs();
        if (!isDirCreated) {
            Log.e("TAG", "Failed to create image directory");
            return "";
        }
    }

    try {
        // 用时间戳做文件名,避免重复
        File imageFile = new File(wallpaperDirectory, Calendar.getInstance().getTimeInMillis() + ".jpg");
        
        // 这里可以省略createNewFile(),因为FileOutputStream会自动创建不存在的文件
        FileOutputStream fo = new FileOutputStream(imageFile);
        fo.write(bytes.toByteArray());
        fo.flush();
        fo.close();

        // 通知媒体扫描器,让图片能在图库中显示
        MediaScannerConnection.scanFile(this, new String[]{imageFile.getPath()}, new String[]{"image/jpeg"}, (path, uri) -> {
            Log.d("TAG", "Image scanned successfully: " + path);
        });

        Log.d("TAG", "File Saved::--->" + imageFile.getAbsolutePath());
        return imageFile.getAbsolutePath();
    } catch (IOException e) {
        e.printStackTrace();
        Log.e("TAG", "Save image failed: " + e.getMessage());
    }
    return "";
}

如果需要保存到公共目录(比如DCIM)

如果一定要把图片存到公共目录(让用户在图库直接看到),Android 10+必须用MediaStore API,不能直接创建文件。示例代码如下:

@RequiresApi(api = Build.VERSION_CODES.Q)
public String saveImageToPublicDCIM(Bitmap myBitmap) {
    ContentValues contentValues = new ContentValues();
    contentValues.put(MediaStore.Images.Media.DISPLAY_NAME, Calendar.getInstance().getTimeInMillis() + ".jpg");
    contentValues.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
    // 指定保存到DCIM下的子目录
    contentValues.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_DCIM + "/MyAppImages");

    // 插入到MediaStore
    Uri imageUri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
    if (imageUri == null) {
        Log.e("TAG", "Failed to create MediaStore entry");
        return "";
    }

    try (OutputStream outputStream = getContentResolver().openOutputStream(imageUri)) {
        myBitmap.compress(Bitmap.CompressFormat.JPEG, 90, outputStream);
        outputStream.flush();
        return imageUri.toString(); // 返回Uri,后续可以用这个Uri访问图片
    } catch (IOException e) {
        e.printStackTrace();
        // 创建失败,删除MediaStore中的无效条目
        getContentResolver().delete(imageUri, null, null);
        return "";
    }
}

最后再检查两个点:

  • 权限:Android 9及以下需要在Manifest中声明WRITE_EXTERNAL_STORAGE,并且动态申请权限;Android 10+不需要这个权限(应用专属目录)。
  • 测试设备:如果是Android 10+的设备,不要用Environment.getExternalStorageDirectory()直接操作公共目录,系统会限制。

内容的提问来源于stack exchange,提问作者Abdo Rabah

火山引擎 最新活动