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

Xamarin.Forms Android 复制图片到公共目录时添加EXIF元数据

问题背景

在基于Xamarin.Forms开发跨平台应用时,需要实现三项核心功能:

  • 拍摄照片并保存到本地存储
  • 为拍摄的图片添加各类元数据
  • 将携带完整元数据的图片副本存储到全局可访问的公共目录,方便用户将图片添加到邮件或通过其他方式分享

其中第一项功能可通过Xamarin.Forms原生能力在Android、iOS、UWP三端轻松实现;第二项功能可通过DependencyService在各平台分别实现,Android平台原本通过如下代码访问图片的EXIF属性值:

ExifInterface newExif = new ExifInterface(filePath);

设置完所需的全部EXIF属性值后,将修改保存回原图片即可。第三项功能同样通过DependencyService实现,将图片副本写入系统公共目录(例如Pictures相册目录)。

目前遇到的问题是:无法确定如何访问写入公共目录后的目标图片,为其添加EXIF数据。

当前已实现的公共目录文件保存代码如下:

public void SaveFileToPublicDirectory(string internalPath, string name)
{
    string path = "";
    try 
    {                
        // 从本地文件路径解码位图
        Bitmap bitmap = BitmapFactory.DecodeFile(internalPath);
        System.IO.Stream fos;

        // 适配Android Q及以上版本
        if (Build.VERSION.SdkInt >= BuildVersionCodes.Q)
        {
            ContentValues contentValues = new ContentValues();
            contentValues.Put(MediaStore.IMediaColumns.DisplayName, name);
            contentValues.Put(MediaStore.IMediaColumns.MimeType, "image/jpg");
            contentValues.Put(MediaStore.IMediaColumns.RelativePath,
                Android.OS.Environment.DirectoryPictures);

            var imageUri = Android.App.Application.Context.ContentResolver.
                Insert(MediaStore.Images.Media.ExternalContentUri, contentValues);
            fos = Android.App.Application.Context.ContentResolver.OpenOutputStream(imageUri);
            bitmap.Compress(Bitmap.CompressFormat.Jpeg, 100, fos);

            path = imageUri.ToString();
            Log.Debug("i-Trax Droid", "Path for saving file in Pictures = " + path);
            // 得到的路径格式类似:content:/media/external/images/media/594
        }
        else
        {
            String imagesDir = Android.OS.Environment.GetExternalStoragePublicDirectory(
                Android.OS.Environment.DirectoryPictures).ToString();
            path = System.IO.Path.Combine(imagesDir, name);
            fos = new FileStream(path, FileMode.Create);
            bitmap.Compress(Bitmap.CompressFormat.Jpeg, 100, fos);
            Util.PathFilenameForPicturesDir_Android = path;
        }
    }
    catch (Exception ex)
    {
        string msg = string.Format("** Exception in SaveFileToPublicDirectory: {0} ", ex.ToString());
        System.Diagnostics.Debug.WriteLine(msg);
        Util.WriteExceptionToSQL(msg);
    }

    return;
}

在Android Q及以上版本中,上述代码执行后得到的图片路径为类似content:/media/external/images/media/594的Content Uri格式,需要获取刚写入文件的ExifInterface实例,读取、修改EXIF数据后保存回文件。


解决方案

AndroidX的ExifInterface从1.3.0版本开始已经支持直接通过FileDescriptor从Content Uri初始化,不需要转换真实文件路径,适配分区存储规则,具体实现如下:

核心实现逻辑

  1. 首先确认Xamarin.Android项目已引用Xamarin.AndroidX.ExifInterface包,版本不低于1.3.0,低版本不支持FileDescriptor初始化方式。
  2. 插入MediaStore拿到imageUri后,不要只存Uri的字符串形式,保留Uri对象,通过ContentResolver打开可读写模式的FileDescriptor,直接传入ExifInterface构造函数。
  3. 完成EXIF属性修改后调用SaveAttributes()即可自动将修改写回Uri对应的文件,无需额外做路径转换。

Android Q+版本EXIF写入代码示例:

// 传入Insert操作返回的imageUri对象,不要用ToString()转换后的字符串
using (var parcelFileDescriptor = Android.App.Application.Context.ContentResolver.OpenFileDescriptor(imageUri, "rw"))
{
    if (parcelFileDescriptor != null)
    {
        // 直接通过文件描述符初始化ExifInterface
        ExifInterface exif = new ExifInterface(parcelFileDescriptor.FileDescriptor);
        // 按需设置EXIF属性,以下为常用示例
        exif.SetAttribute(ExifInterface.TagDatetime, DateTime.Now.ToString("yyyy:MM:dd HH:mm:ss"));
        exif.SetAttribute(ExifInterface.TagMake, "Device Brand");
        exif.SetAttribute(ExifInterface.TagModel, "Device Model");
        // 保存修改,直接回写到公共目录对应的文件
        exif.SaveAttributes();
    }
}

注意:打开FileDescriptor时必须指定模式为"rw"(读写模式),传只读模式会导致SaveAttributes抛出权限异常。

低版本兼容

Android Q以下版本的逻辑不需要调整,原有代码已经能拿到公共目录文件的绝对路径,直接用new ExifInterface(filePath)的方式初始化、修改、保存EXIF即可。

优化建议

当前实现先将原图解码为Bitmap再重新压缩为JPG写入公共目录,会丢失原文件所有EXIF信息,同时二次压缩会造成画质损失,更合理的处理方式是:

  • 跳过Bitmap解码、压缩步骤,直接将内部存储的原文件流复制到公共目录对应的输出流,保留原始文件数据
  • 流操作统一加using块或手动调用Close()、Dispose()释放句柄,避免文件占用导致EXIF写入失败
  • 文件复制完成后再通过上述ExifInterface方法补充、修改需要的元数据。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 11:06:28