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

Android 13下.NET MAUI如何将图片保存到自定义相册文件夹

解决.NET MAUI拍照图片无法在系统相册显示的问题

问题核心

当前图片保存到了应用私有存储目录(/data/user/0/com.companyname.photov2/files/Pictures/GPV2/...),系统相册不会自动扫描应用私有目录下的文件,因此无法被识别显示。

解决方案

方案1:使用MediaStore API保存(符合Android Scoped Storage规范,推荐)

Android 10及以上引入了Scoped Storage限制,直接写入公共目录可能存在权限问题,通过MediaStore将图片插入系统媒体库,可自动在相册中显示:

using Android.Content;
using Android.OS;
using Android.Provider;
using System.IO;
using System.Threading.Tasks;

public async Task SavePhotoToGallery(Xamarin.MediaGallery.Photo photo, int visitaId)
{
    var context = Android.App.Application.Context;
    string fileName = $"{visitaId}_{DateTime.Now:yy_MM_dd_HH_mm_ss}.jpg";

    // 配置MediaStore元数据
    ContentValues values = new ContentValues();
    values.Put(MediaStore.Images.ImageColumns.DisplayName, fileName);
    values.Put(MediaStore.Images.ImageColumns.MimeType, "image/jpeg");
    
    if (Build.VERSION.SdkInt >= BuildVersionCodes.Q)
    {
        // Android 10+ 指定相对路径到Pictures/GPV2
        values.Put(MediaStore.Images.ImageColumns.RelativePath, $"Pictures/GPV2");
        values.Put(MediaStore.Images.ImageColumns.IsPending, 1);
    }
    else
    {
        // Android 9及以下直接指定公共存储路径
        string publicPicturesDir = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryPictures).AbsolutePath;
        string folderPath = Path.Combine(publicPicturesDir, "GPV2");
        Directory.CreateDirectory(folderPath);
        string filePath = Path.Combine(folderPath, fileName);
        values.Put(MediaStore.Images.ImageColumns.Data, filePath);
    }

    // 插入到媒体库并获取文件URI
    var uri = context.ContentResolver.Insert(MediaStore.Images.Media.ExternalContentUri, values);
    
    // 将拍摄的图片写入媒体库
    using (Stream sourceStream = await photo.OpenReadAsync())
    using (Stream outputStream = context.ContentResolver.OpenOutputStream(uri))
    {
        await sourceStream.CopyToAsync(outputStream);
    }

    // 标记文件为已完成(Android 10+)
    if (Build.VERSION.SdkInt >= BuildVersionCodes.Q)
    {
        values.Clear();
        values.Put(MediaStore.Images.ImageColumns.IsPending, 0);
        context.ContentResolver.Update(uri, values, null, null);
    }

    // 将图片字节读取到vf.Foto(如果需要本地缓存)
    using (Stream inputStream = context.ContentResolver.OpenInputStream(uri))
    using (MemoryStream ms = new MemoryStream())
    {
        await inputStream.CopyToAsync(ms);
        vf.Foto = ms.ToArray();
    }
    vf.Titulo = fileName;
}

方案2:保存到公共目录后通知系统扫描

如果坚持直接写入公共存储目录,需切换路径到系统公共Pictures文件夹,再通知媒体库扫描新文件:

步骤1:修改保存路径

替换原私有路径为公共外部存储路径:

string publicPicturesDir;
if (Build.VERSION.SdkInt >= BuildVersionCodes.Q)
{
    publicPicturesDir = Android.OS.Environment.DirectoryPictures;
}
else
{
    publicPicturesDir = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryPictures).AbsolutePath;
}
var folderPath = Path.Combine(publicPicturesDir, "GPV2");
Directory.CreateDirectory(folderPath);
string filePath = Path.Combine(folderPath, $"{vf.IdVisita}_{DateTime.Now:yy_MM_dd_HH_mm_ss}.jpg");

步骤2:保存后通知系统扫描

写入文件后发送广播或调用扫描接口:

// 写入图片到文件(原逻辑补充)
using (Stream sourceStream = await photo.OpenReadAsync())
using (FileStream fileStream = new FileStream(filePath, FileMode.Create))
{
    await sourceStream.CopyToAsync(fileStream);
}

// 通知媒体库更新
if (Build.VERSION.SdkInt >= BuildVersionCodes.R)
{
    MediaScannerConnection.ScanFile(context, new string[] { filePath }, null, null);
}
else
{
    Intent mediaScanIntent = new Intent(Intent.ActionMediaScannerScanFile);
    mediaScanIntent.SetData(Android.Net.Uri.FromFile(new Java.IO.File(filePath)));
    context.SendBroadcast(mediaScanIntent);
}

权限优化建议

  • 移除MANAGE_EXTERNAL_STORAGE权限:此权限用于访问所有文件,本需求无需开启,会增加权限申请复杂度。
  • Android 13+仅需保留CAMERA和READ_MEDIA_IMAGES权限,写入媒体库或公共目录无需额外写入权限。

内容的提问来源于stack exchange,提问作者Giorgio

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 02:18:21