批量扫描JPG图片EXIF编程更新:GPS纬度设置问题求助
解决EXIF GPS坐标设置失败的问题
我明白你现在遇到的麻烦——其他EXIF数据都能正常更新,唯独GPS坐标死活设不上,用字符串格式写入完全不管用对吧?这是因为EXIF里的GPS纬度/经度根本不是存储为你尝试的那种字符串格式,而是采用二进制的有理数组(SRATIONAL类型)来存储的。
问题根源
EXIF标准中,GPSLatitude(标签ID:0x0002)和GPSLongitude(标签ID:0x0004)的类型是SRATIONAL(有符号有理数组),每个坐标由「度、分、秒」三个部分组成,每个部分都需要用「分子+分母」的4字节整数对来表示(也就是每个成分占8字节:4字节分子,4字节分母)。所以你直接写入"42/1,5/1,33/1"这种字符串的字节,完全不符合EXIF的解析规则,自然不会生效。
正确的实现方式(基于System.Drawing)
下面是针对你场景的完整代码示例,我们手动构建符合要求的二进制字节数组来设置GPS纬度:
首先,需要一个辅助方法把整数转成大端字节序的数组(因为EXIF采用大端字节序存储数据):
private static byte[] IntToBigEndianBytes(int value) { byte[] bytes = BitConverter.GetBytes(value); if (BitConverter.IsLittleEndian) Array.Reverse(bytes); return bytes; }
然后是设置GPS纬度和纬度参考(北纬/南纬)的核心代码:
// 假设srcImg是你的Image对象 PropertyItem gpsLatitudeItem; // 尝试获取现有GPS纬度标签,不存在则克隆一个现有标签来创建 try { gpsLatitudeItem = srcImg.GetPropertyItem(2); // 0x0002对应GPSLatitude } catch (ArgumentException) { var dummyItem = srcImg.PropertyItems.FirstOrDefault(); if (dummyItem == null) throw new InvalidOperationException("图片没有可克隆的现有属性项,请先确保图片有其他EXIF数据"); gpsLatitudeItem = (PropertyItem)dummyItem.Clone(); } // 配置GPSLatitude的属性 gpsLatitudeItem.Id = 2; gpsLatitudeItem.Type = 5; // 5对应SRATIONAL类型 gpsLatitudeItem.Len = 24; // 3个有理值 × 8字节(分子+分母)=24字节 // 构建度、分、秒的有理值字节(以42°5'33"为例) byte[] degreeNum = IntToBigEndianBytes(42); // 度的分子:42 byte[] degreeDen = IntToBigEndianBytes(1); // 度的分母:1 byte[] minuteNum = IntToBigEndianBytes(5); // 分的分子:5 byte[] minuteDen = IntToBigEndianBytes(1); // 分的分母:1 byte[] secondNum = IntToBigEndianBytes(33); // 秒的分子:33 byte[] secondDen = IntToBigEndianBytes(1); // 秒的分母:1 // 拼接成完整的Value字节数组 List<byte> valueBytes = new List<byte>(); valueBytes.AddRange(degreeNum); valueBytes.AddRange(degreeDen); valueBytes.AddRange(minuteNum); valueBytes.AddRange(minuteDen); valueBytes.AddRange(secondNum); valueBytes.AddRange(secondDen); gpsLatitudeItem.Value = valueBytes.ToArray(); srcImg.SetPropertyItem(gpsLatitudeItem); // 别忘了设置纬度参考:北纬用"N",南纬用"S" PropertyItem gpsLatRefItem; try { gpsLatRefItem = srcImg.GetPropertyItem(1); // 0x0001对应GPSLatitudeRef } catch (ArgumentException) { var dummyItem = srcImg.PropertyItems.FirstOrDefault(); if (dummyItem == null) throw new InvalidOperationException("图片没有可克隆的现有属性项"); gpsLatRefItem = (PropertyItem)dummyItem.Clone(); } gpsLatRefItem.Id = 1; gpsLatRefItem.Type = 2; // 2对应ASCII类型 gpsLatRefItem.Len = 1; gpsLatRefItem.Value = Encoding.ASCII.GetBytes("N"); srcImg.SetPropertyItem(gpsLatRefItem);
更简单的替代方案(用ImageSharp库)
如果不想手动处理繁琐的字节数组,推荐使用更现代的ImageSharp库,它已经封装了EXIF GPS的处理逻辑,代码会简洁很多:
using SixLabors.ImageSharp; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Formats.Jpeg; // 加载图片 using (var image = Image.Load("你的图片路径.jpg")) { // 设置GPS纬度:度、分、秒分别用有理值表示 image.Metadata.ExifProfile.SetValue(ExifTag.GPSLatitude, new Rational[] { new Rational(42, 1), new Rational(5, 1), new Rational(33, 1) }); // 设置纬度参考 image.Metadata.ExifProfile.SetValue(ExifTag.GPSLatitudeRef, "N"); // 保存修改后的图片 image.Save("输出图片路径.jpg", new JpegEncoder()); }
这样就能正确把GPS坐标写入图片的EXIF数据里了,经度的设置逻辑和纬度完全一致,只需要把标签ID换成0x0004(GPSLongitude),参考标签换成0x0003(GPSLongitudeRef,东经用"E",西经用"W")即可。
内容的提问来源于stack exchange,提问作者mrb398
相关产品推荐
相关产品推荐

