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

从ImageView保存图片至相册时触发File Not Found Exception问题求助

解决ImageView图片保存到相册的File Not Found Exception问题

嘿,别担心,作为初学者遇到这种问题很正常,我来帮你一步步搞定这个File Not Found Exception的问题!

为什么会出现这个错误?

你的代码在低版本Android上可能能跑,但在Android 6.0(API 23)及以上会因为权限问题报错,而Android 10(API 29)及以上还会因为存储策略变更导致直接操作外部目录被限制,最终触发文件找不到的异常。

一步步解决方法

1. 先添加必要的权限

首先在AndroidManifest.xml里添加存储权限:

<!-- 基础存储权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- Android 13及以上需要额外的图片权限 -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />

2. 动态申请权限(API 23+必须)

从Android 6.0开始,危险权限需要在运行时申请,不能只靠Manifest。在你的MainActivity里添加以下代码:

private static final int REQUEST_STORAGE_PERMISSION = 100;

// 检查并申请存储权限
private boolean checkStoragePermission() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
                == PackageManager.PERMISSION_GRANTED) {
            return true;
        } else {
            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                    REQUEST_STORAGE_PERMISSION);
            return false;
        }
    }
    // 低版本默认有权限
    return true;
}

// 处理权限申请结果
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == REQUEST_STORAGE_PERMISSION) {
        if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            // 权限通过,执行保存操作
            saveImageToGallery();
        } else {
            Toast.makeText(this, "需要存储权限才能保存图片哦", Toast.LENGTH_SHORT).show();
        }
    }
}

3. 适配不同Android版本的保存逻辑

Android 10及以上引入了Scoped Storage,不能直接写入外部存储的自定义目录,我们需要用MediaStore来插入图片;低版本则可以继续用传统方式。替换你原来的保存代码为:

private void saveImageToGallery() {
    BitmapDrawable draw = (BitmapDrawable) mainImage.getDrawable();
    Bitmap bitmap = draw.getBitmap();

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        // Android Q及以上:用MediaStore保存
        ContentValues values = new ContentValues();
        values.put(MediaStore.Images.Media.DISPLAY_NAME, System.currentTimeMillis() + ".jpg");
        values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
        // 保存到系统图片目录下的MyFolder文件夹
        values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + "/MyFolder");

        Uri uri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
        if (uri != null) {
            try {
                OutputStream outputStream = getContentResolver().openOutputStream(uri);
                bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
                if (outputStream != null) {
                    outputStream.close();
                    Toast.makeText(this, "图片保存成功啦!", Toast.LENGTH_SHORT).show();
                }
            } catch (IOException e) {
                e.printStackTrace();
                Toast.makeText(this, "保存失败:" + e.getMessage(), Toast.LENGTH_SHORT).show();
            }
        }
    } else {
        // Android Q以下:传统文件保存方式
        File pictureDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
        File myFolder = new File(pictureDir.getAbsolutePath() + "/MyFolder");
        // 创建文件夹(如果不存在)
        if (!myFolder.exists()) {
            myFolder.mkdirs();
        }
        String fileName = String.format("%d.jpg", System.currentTimeMillis());
        File outFile = new File(myFolder, fileName);

        try {
            FileOutputStream outStream = new FileOutputStream(outFile);
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outStream);
            outStream.flush();
            outStream.close();

            // 通知相册扫描新图片
            Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
            intent.setData(Uri.fromFile(outFile));
            sendBroadcast(intent);

            Toast.makeText(this, "图片保存成功啦!", Toast.LENGTH_SHORT).show();
        } catch (Exception e) {
            e.printStackTrace();
            Toast.makeText(this, "保存失败:" + e.getMessage(), Toast.LENGTH_SHORT).show();
        }
    }
}

4. 修改你的点击事件

把原来的点击事件改成先检查权限,再执行保存:

save.setOnClickListener(new View.OnClickListener(){
    @Override
    public void onClick(View v){
        if (checkStoragePermission()) {
            saveImageToGallery();
        }
    }
});

关键注意点

  • 权限是核心:如果没申请动态权限,App没有写入存储的权限,就会触发File Not Found Exception。
  • 适配Scoped Storage:Android 10+不能直接写外部根目录,用MediaStore是官方推荐的方式,也能确保图片被相册正确识别。
  • 用系统目录:Environment.DIRECTORY_PICTURES是系统默认的图片存储目录,比直接写根目录更安全,也符合系统规范。

现在你可以测试一下,应该就能正常保存图片到相册啦!

内容的提问来源于stack exchange,提问作者Muhammad Ramzan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 13:12:35