Android新手求助:调用系统相机拍照无法保存至DCIM目录
嘿,我来帮你搞定这个问题~你当前遇到的核心问题是调用系统相机拍照后,目标文件是空的,这主要是Uri处理不符合Android版本要求,再加上目录创建和文件名的小问题导致的,咱们一步步来修正:
一、先修正目录创建逻辑(改为DCIM目录)
你原本想把图片存到DCIM目录,但代码里用了Environment.DIRECTORY_PICTURES(这是系统默认的"图片"目录),而且目录创建的代码也有问题,改成下面这样:
private void CreateDirectoryForPictures() { // 改用DCIM目录,符合你的需求 File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM); App.directory = new File(path, "Mahapach_Images"); // 直接用mkdirs()创建所有不存在的父目录,不需要getParentFile() if (!App.directory.exists()) { boolean isCreated = App.directory.mkdirs(); // 加个日志确认目录是否创建成功,方便排查 Log.d("Camera", "目录创建结果:" + isCreated); } }
二、给图片文件名加上扩展名
你当前的imageFileName没有.jpg后缀,系统相机可能无法识别正确的文件格式,导致无法写入内容,修改文件名生成逻辑:
String imageFileName = "JPEG_" + timeStamp + ".jpg"; // 必须加上.jpg后缀
三、修复跨应用Uri传递问题(Android 7.0+关键)
从Android 7.0开始,系统禁止直接传递file://格式的Uri给其他应用(比如系统相机),否则会抛出FileUriExposedException,必须用FileProvider生成content://格式的安全Uri:
1. 在AndroidManifest.xml中注册FileProvider
在<application>标签内添加以下配置:
<provider android:name="androidx.core.content.FileProvider" android:authorities="${applicationId}.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider>
(如果用的是旧版support库,把androidx.core.content.FileProvider换成android.support.v4.content.FileProvider)
2. 创建file_paths.xml配置文件
在res/xml目录下新建file_paths.xml(没有xml目录就手动创建),内容如下:
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <!-- 共享DCIM目录下的自定义子文件夹 --> <external-path name="dcim_mahapach" path="DCIM/Mahapach_Images/" /> </paths>
3. 修改takeAPicture方法中的Uri生成逻辑
替换原来的Uri.fromFile(App.file),改用FileProvider生成Uri,并添加权限:
private void takeAPicture(){ String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date()); String imageFileName = "JPEG_" + timeStamp + ".jpg"; // 已加后缀 CreateDirectoryForPictures(); Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); App.file = new File(App.directory, imageFileName); // 生成安全的content:// Uri Uri photoUri = FileProvider.getUriForFile( this, getPackageName() + ".fileprovider", // 和Manifest里的authorities保持一致 App.file ); intent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri); // 给相机应用授予读写Uri的权限,否则无法写入文件 intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); // 先判断相机应用是否存在,避免崩溃 if (intent.resolveActivity(getPackageManager()) != null) { startActivityForResult(intent, 1); } }
四、补充权限处理(针对Android 6.0+)
如果你的应用目标SDK是23及以上,需要动态申请WRITE_EXTERNAL_STORAGE权限(Android 10及以上启用分区存储后,这个权限可以不用,但为了兼容旧版本还是建议加上):
private static final int REQUEST_PERMISSION = 100; // 调用拍照前先检查权限 private void checkPermissionAndTakePhoto() { if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_PERMISSION); } else { // 权限已授予,执行拍照逻辑 takeAPicture(); } } // 处理权限申请结果 @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == REQUEST_PERMISSION) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { takeAPicture(); } else { // 用户拒绝权限,提示无法保存图片 Toast.makeText(this, "需要存储权限才能保存图片", Toast.LENGTH_SHORT).show(); } } }
最后验证修改后的onActivityResult
媒体扫描的逻辑可以保留,但现在相机拍照后应该能正确写入图片到你指定的DCIM子目录了。
内容的提问来源于stack exchange,提问作者Itzik Pichhadze




