如何从相机拍摄所得的URI中打开Bitmap?
解决Xamarin Android中相机拍照后URI无法使用BitmapFactory.DecodeFile的问题
我太懂你这种卡壳的焦虑了!Xamarin Android的官方相机指南确实经常有缺漏和过时内容,好不容易走到图片解码这步,还碰到URI没法直接用BitmapFactory.DecodeFile的坎儿,试了一堆方案还是没搞定真的闹心。
针对你遇到的情况,这里有两个亲测有效的解决思路,你可以根据自己的场景选:
思路1:把URI转换成真实文件路径再解码
如果你一定要用BitmapFactory.DecodeFile,那得先把相机返回的content://类型URI转换成真实文件路径。这里要适配不同Android版本的URI规则,写个工具方法就能搞定:
public string GetRealPathFromUri(Context context, Android.Net.Uri uri) { string path = null; // 处理MediaStore的content类型URI if ("content".Equals(uri.Scheme, StringComparison.OrdinalIgnoreCase)) { string[] projection = { MediaStore.Images.Media.InterfaceConsts.Data }; using (var cursor = context.ContentResolver.Query(uri, projection, null, null, null)) { if (cursor != null && cursor.MoveToFirst()) { int columnIndex = cursor.GetColumnIndexOrThrow(MediaStore.Images.Media.InterfaceConsts.Data); path = cursor.GetString(columnIndex); } } } // 处理file类型的URI else if ("file".Equals(uri.Scheme, StringComparison.OrdinalIgnoreCase)) { path = uri.Path; } return path; }
拿到路径后就可以正常调用BitmapFactory.DecodeFile(path)了,但要注意Android 10及以上的Scoped Storage限制——如果用户拍摄的照片存在应用私有目录或MediaStore受保护区域,这种方法可能会有权限问题,这时候思路2会更稳妥。
思路2:直接通过URI获取输入流解码(推荐)
不用转路径,直接从URI获取输入流,用BitmapFactory.DecodeStream解码Bitmap,完全避开路径权限的坑,适配所有Android版本,还能顺便处理你要调整图片大小的需求:
public async Task<Bitmap> DecodeBitmapFromUri(Context context, Android.Net.Uri uri, int reqWidth, int reqHeight) { // 先获取输入流,只读取图片尺寸避免OOM using (var inputStream = await context.ContentResolver.OpenInputStreamAsync(uri)) { var options = new BitmapFactory.Options { InJustDecodeBounds = true }; BitmapFactory.DecodeStream(inputStream, null, options); // 计算合适的缩放比例 options.InSampleSize = CalculateInSampleSize(options, reqWidth, reqHeight); options.InJustDecodeBounds = false; // 重新打开输入流(第一次解码后流指针会移动到末尾) using (var inputStream2 = await context.ContentResolver.OpenInputStreamAsync(uri)) { return BitmapFactory.DecodeStream(inputStream2, null, options); } } } // 计算缩放比例,保证图片缩小到目标尺寸且不浪费内存 private int CalculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { int height = options.OutHeight; int width = options.OutWidth; int inSampleSize = 1; if (height > reqHeight || width > reqWidth) { int halfHeight = height / 2; int halfWidth = width / 2; while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) { inSampleSize *= 2; } } return inSampleSize; }
额外踩坑提醒
- 权限要配全:在
AndroidManifest.xml里添加相机和媒体读取权限,Android 13+记得用READ_MEDIA_IMAGES替代READ_EXTERNAL_STORAGE; - 相机启动时要正确设置返回结果,确保拿到的URI是有效的;
- 用完Bitmap记得调用
Recycle()释放内存,避免内存泄漏。
内容的提问来源于stack exchange,提问作者RhomburVernius
相关产品推荐
相关产品推荐

