从MediaStore检索图片为空:安卓相机拍照上传Firebase Storage失败
解决相机拍照后图片Uri返回null并上传Firebase Storage的问题
嘿,我注意到你在调用相机拍照后,尝试获取图片Uri时一直得到null,导致没法正常上传到Firebase Storage。咱们来拆解下代码里的问题,然后一步步解决它。
问题出在哪?
你在onActivityResult的PHOTO_CAMERA_IMAGE分支里犯了一个核心错误:当相机拍照完成返回后,你重新创建了一个全新的takePictureIntent,还试图从这个刚创建的Intent里获取图片Uri——这肯定会得到null啊!因为这个新Intent根本没有相机返回的数据。
正确的逻辑应该是:在启动相机之前,就创建好存储图片的文件,并把这个文件的Uri通过MediaStore.EXTRA_OUTPUT传给相机,这样相机就会把照片直接保存到你指定的路径,后续在回调里直接用这个预先生成的Uri就行。
修正后的完整代码实现
1. 先定义全局变量
在你的MainChatActivity里添加这两个全局变量:
private Uri photoURI; private static final int PHOTO_CAMERA_IMAGE = 1; // 和你之前定义的常量保持一致
2. 重写启动相机的方法
把文件创建和Uri配置的逻辑移到启动相机的方法里,而不是回调中:
private void camera() { // 先检查相机权限 if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) { Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); // 确保有相机应用能处理这个Intent if (takePictureIntent.resolveActivity(getPackageManager()) != null) { File photoFile = null; try { // 创建存储照片的临时文件 photoFile = createImageFile(); } catch (IOException e) { // 处理文件创建失败的异常,比如弹个Toast提示用户 e.printStackTrace(); Toast.makeText(this, "创建图片文件失败", Toast.LENGTH_SHORT).show(); } // 只有文件创建成功,才继续启动相机 if (photoFile != null) { // 通过FileProvider生成安全的Uri photoURI = FileProvider.getUriForFile(this, "borealos.com.chat.fileprovider", photoFile); // 关键:告诉相机把照片保存到这个Uri对应的路径 takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI); startActivityForResult(takePictureIntent, PHOTO_CAMERA_IMAGE); } } } else { // 如果没有相机权限,主动请求权限 ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, 100); } } // 生成唯一图片文件的方法(确保这个方法可用) private File createImageFile() throws IOException { // 用时间戳生成唯一文件名,避免重复 String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date()); String imageFileName = "JPEG_" + timeStamp + "_"; // 存在应用私有外部存储的Pictures目录,不需要额外存储权限(Android 10+) File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES); return File.createTempFile(imageFileName, ".jpg", storageDir); }
3. 修正onActivityResult回调
现在回调里只需要直接用预先生成的photoURI就行,不用再从data里找:
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); switch (requestCode) { case PICK_IMAGE: if (resultCode == RESULT_OK && data != null) { uploadImage(data.getData()); } break; case PHOTO_CAMERA_IMAGE: if (resultCode == RESULT_OK) { // 因为已经指定了EXTRA_OUTPUT,直接用之前的photoURI if (photoURI != null) { // 通知系统扫描新图片,让图库能显示这张照片 sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, photoURI)); // 上传到Firebase Storage uploadImage(photoURI); } } break; } }
4. 别忘了配置FileProvider
确保你的AndroidManifest.xml里正确配置了FileProvider,否则会报错:
<application ...> <!-- FileProvider配置 --> <provider android:name="androidx.core.content.FileProvider" android:authorities="borealos.com.chat.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider> ... </application>
然后在res/xml目录下创建file_paths.xml文件(如果没有xml目录就新建一个):
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <!-- 对应我们用的getExternalFilesDir(Environment.DIRECTORY_PICTURES) --> <external-files-path name="my_images" path="Pictures" /> </paths>
几个关键提醒
- 当使用
MediaStore.EXTRA_OUTPUT时,相机返回的dataIntent会是null,这是正常现象,因为照片已经直接保存到你指定的路径了,不用从data里取。 - Android 10及以上版本,使用
getExternalFilesDir存储图片不需要WRITE_EXTERNAL_STORAGE权限,因为这是应用私有目录;如果是Android 9及以下,可能需要申请这个权限。 - 上传Firebase Storage的
uploadImage方法里,确保你正确处理了Uri对应的文件,比如用FileInputStream读取文件,或者直接用Firebase的putFile(photoURI)方法,这个方法支持直接传入Uri。
这样修改后,你就能正确获取到拍照后的图片Uri,顺利上传到Firebase Storage了!
内容的提问来源于stack exchange,提问作者Victor Torrens
相关产品推荐
相关产品推荐

