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
相关产品推荐
相关产品推荐

