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

Android中File.createTempFile()结合Environment.getExternalStorageDirectory()调用失败

解决Android相机拍照保存到系统相册的问题

嘿,我明白你现在的困扰——官方指南只讲了私有目录的保存方法,但你需要把照片存到系统相册里,而且用Environment.getExternalStorageDirectory()结合File.createTempFile()还失败了。这主要是因为Android 10之后引入了Scoped Storage,旧的外部存储API被废弃且限制访问了,我给你分版本梳理解决方案:

一、Android 10+(API 29及以上):用MediaStore直接插入

从Android 10开始,直接操作外部存储根目录会被系统限制,正确的做法是通过MediaStore将照片插入到系统相册对应的媒体库中,这样不仅能绕过权限问题,照片还会直接出现在相册里。

步骤1:构建相机Intent并获取保存用的Uri

private Uri photoUri;
private static final int REQUEST_IMAGE_CAPTURE = 1;

public void dispatchTakePictureIntent(View view) {
    // 初始化相机Intent
    Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
        // 准备MediaStore插入参数,定义照片的文件名、类型和存储路径
        ContentValues values = new ContentValues();
        values.put(MediaStore.Images.Media.DISPLAY_NAME, "photo_" + System.currentTimeMillis() + ".jpg");
        values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
        // Android 10+可以指定相对路径,比如Pictures目录下的自定义文件夹
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + "/MyAppPhotos");
        }

        // 插入到媒体库并获取对应的Uri
        photoUri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
        if (photoUri != null) {
            // 将Uri传给相机Intent,指定照片保存位置
            takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
            startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
        }
    }
}

步骤2:拍照完成后通知系统刷新相册

虽然用MediaStore插入的图片会自动被系统识别,但为了确保照片立刻显示在相册里,可以在onActivityResult中触发媒体扫描:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
        // 通知系统扫描新图片
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            MediaScannerConnection.scanFile(this, new String[]{photoUri.getPath()}, null,
                    (path, uri) -> {
                        // 扫描完成后的回调,可添加后续处理逻辑
                    });
        } else {
            // 低版本通过广播触发扫描
            sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, photoUri));
        }
    }
}

二、Android 9及以下(API 28及以下):兼容旧存储方式

对于低版本系统,你需要先申请WRITE_EXTERNAL_STORAGE权限,然后使用公共图片目录来创建文件:

步骤1:权限检查与申请

private static final int REQUEST_PERMISSION_WRITE_STORAGE = 2;

public void dispatchTakePictureIntent(View view) {
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
            != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(this,
                new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                REQUEST_PERMISSION_WRITE_STORAGE);
        return;
    }
    // 权限已授予,继续创建文件并启动相机
    createImageFileAndLaunchCamera();
}

步骤2:创建图片文件并启动相机

private File photoFile;

private void createImageFileAndLaunchCamera() {
    try {
        // 生成唯一文件名
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
        String imageFileName = "JPEG_" + timeStamp + "_";
        // 获取系统公共图片目录
        File storageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
        // 创建临时文件
        photoFile = File.createTempFile(
                imageFileName,  /* 文件前缀 */
                ".jpg",         /* 文件后缀 */
                storageDir      /* 存储目录 */
        );

        // 适配Android 7+的FileUriExposedException问题
        Uri photoUri;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            photoUri = FileProvider.getUriForFile(this,
                    "com.your.package.name.fileprovider", // 替换为你的FileProvider授权名
                    photoFile);
        } else {
            photoUri = Uri.fromFile(photoFile);
        }

        // 启动相机
        Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
        startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
    } catch (IOException ex) {
        // 创建文件失败的错误处理
        ex.printStackTrace();
    }
}

步骤3:拍照后刷新相册

同样在onActivityResult中发送广播通知系统扫描新照片:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
        sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(photoFile)));
    }
}

关键注意点

  • 废弃API替代:不要再使用Environment.getExternalStorageDirectory(),Android 10+用MediaStore,低版本用getExternalStoragePublicDirectory()。
  • 权限差异:Android 10+不需要WRITE_EXTERNAL_STORAGE权限(插入MediaStore时自动获得授权),但低版本必须提前申请。
  • FileProvider配置:Android 7+(API 24)如果使用文件Uri,必须在Manifest中配置FileProvider,避免FileUriExposedException。

这样调整后,你的照片就能正常保存到系统相册,同时适配不同Android版本啦!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 08:51:54