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

使用Xamarin.Android在Android 11上保存并读取照片的问题

解决方案:相机拍照存储与访问问题

问题根源分析

  1. Android 7.0+ URI限制:直接使用Uri.FromFile()生成file://格式URI会触发FileUriExposedException,相机应用无法访问该路径。
  2. Scoped Storage(Android 10+):公共目录(如ExternalStoragePublicDirectory)的直接文件操作被限制,必须通过MediaStoreAPI或申请对应权限才能读写。
  3. 应用私有目录特性:GetExternalFilesDir()属于应用私有外部目录,MediaStore是用于管理公共媒体库的API,无需也不能用它来操作私有目录。

方案一:存储到应用私有外部目录(推荐)

此目录无需额外权限,应用拥有完全读写权限,卸载应用时文件会自动删除,符合隐私规范。

1. 配置FileProvider

在AndroidManifest.xml中添加FileProvider配置:

<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>

在res/xml下新建file_paths.xml,定义共享路径:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-files-path name="belco_pictures" path="Pictures/BelcoPict" />
</paths>

2. 修改拍照代码

private bool DoPhoto(int KeepAsking)
{
    Intent intent = new Intent(MediaStore.ActionImageCapture);
    // 检查是否有可用的相机应用
    if (intent.ResolveActivity(PackageManager) == null)
    {
        return NoPhoto;
    }

    // 创建应用私有目录下的存储文件夹
    var picturesDir = new File(Application.Context.GetExternalFilesDir(Environment.DirectoryPictures), "BelcoPict");
    if (!picturesDir.Exists())
    {
        picturesDir.Mkdirs();
    }
    App._PhotoFile = new File(picturesDir, MyFile);

    // 使用FileProvider生成content://格式的URI
    Uri photoUri = FileProvider.GetUriForFile(
        Application.Context,
        $"{Application.Context.PackageName}.fileprovider",
        App._PhotoFile
    );

    // 授予相机应用临时读写权限
    intent.PutExtra(MediaStore.ExtraOutput, photoUri);
    intent.AddFlags(ActivityFlags.GrantReadUriPermission);
    intent.AddFlags(ActivityFlags.GrantWriteUriPermission);

    StartActivityForResult(intent, KeepAsking);
    return NoPhoto;
}

方案二:存储到公共图片目录并实现访问

1. 权限申请

  • Android 12及以下:动态申请WRITE_EXTERNAL_STORAGE和READ_EXTERNAL_STORAGE权限
  • Android 13及以上:动态申请READ_MEDIA_IMAGES权限(读取),写入公共目录直接用MediaStoreAPI

2. 用MediaStore生成公共目录URI

private async Task<Uri> CreatePublicPhotoUri(string fileName)
{
    ContentValues values = new ContentValues();
    values.Put(MediaStore.Images.Media.Columns.DisplayName, fileName);
    values.Put(MediaStore.Images.Media.Columns.MimeType, "image/jpeg");
    values.Put(MediaStore.Images.Media.Columns.RelativePath, $"{Environment.DirectoryPictures}/BelcoPict");

    return Application.Context.ContentResolver.Insert(MediaStore.Images.Media.ExternalContentUri, values);
}

private bool DoPhoto(int KeepAsking)
{
    Intent intent = new Intent(MediaStore.ActionImageCapture);
    if (intent.ResolveActivity(PackageManager) == null)
    {
        return NoPhoto;
    }

    // 生成公共目录的URI
    var uriTask = CreatePublicPhotoUri(MyFile);
    uriTask.Wait();
    Uri photoUri = uriTask.Result;

    if (photoUri == null)
    {
        return NoPhoto;
    }

    intent.PutExtra(MediaStore.ExtraOutput, photoUri);
    StartActivityForResult(intent, KeepAsking);
    return NoPhoto;
}

3. 读取公共目录照片

通过ContentResolver查询或直接访问URI即可,确保已申请对应权限:

// 示例:通过URI读取图片流
using (var stream = Application.Context.ContentResolver.OpenInputStream(photoUri))
{
    // 处理图片流
}

内容的提问来源于stack exchange,提问作者olivier Goossens Bara

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 10:52:37