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




