Java 提取和删除照片图片 Exif GPS 等隐私信息

本贴最后更新于 1733 天前,其中的信息可能已经时移世改

照片图片 Exif

通过手机相机或者数码相机拍摄的照片都带有 Exif 元数据信息,比如下面这张照片:

IMG20191216134916.jpg

它的 Exif 信息为:

Root: ImageWidth: 4000 ImageLength: 3000 Make: 'Xiaomi' Model: 'MI CC 9' Orientation: 1 XResolution: 72 YResolution: 72 ResolutionUnit: 2 DateTime: '2019:12:16 13:49:17' YCbCrPositioning: 1 ExifOffset: 210 GPSInfo: 770 Exif: ExposureTime: 1/295 (0.003) FNumber: 179/100 (1.79) PhotographicSensitivity: 112 Unknown Tag (0x8895): 0 ExifVersion: 48, 50, 50, 48 DateTimeOriginal: '2019:12:16 13:49:17' DateTimeDigitized: '2019:12:16 13:49:17' ComponentsConfiguration: 1, 2, 3, 0 ShutterSpeedValue: 8202/1000 (8.202) ApertureValue: 167/100 (1.67) ExposureCompensation: 0 MaxApertureValue: 167/100 (1.67) MeteringMode: 2 LightSource: 0 Flash: 16 FocalLength: 4740/1000 (4.74) SubSecTime: '646073' SubSecTimeOriginal: '646073' SubSecTimeDigitized: '646073' Unknown Tag (0x9999): '{"mirror":false,"sensor_type":"rear","Hdr":"off","OpMode":36869}' FlashpixVersion: 48, 49, 48, 48 ColorSpace: 1 ExifImageWidth: 4000 ExifImageLength: 3000 InteropOffset: 738 SensingMethod: 1 WhiteBalance: 0 FocalLengthIn35mmFormat: 25 Interoperability: InteroperabilityIndex: 'R98' InteroperabilityVersion: 48, 49, 48, 48 Gps: GPSLatitudeRef: 'N' GPSLatitude: 40, 21, 401435/10000 (40.144) GPSLongitudeRef: 'E' GPSLongitude: 116, 1, 14916/10000 (1.492) GPSAltitudeRef: 0 GPSAltitude: 722801/1000 (722.801) GPSTimeStamp: 5, 49, 12 GPSProcessingMethod: 'GPS' GPSDateStamp: '2019:12:16'

我们可以看到该照片是在 2019-12-16 13:49:17 通过小米 CC 9 拍摄,并且可以看到经纬度海拔等信息。

如果没有去除 Exif 里的敏感信息,那么发布到网络上后,任何人都可以查看 Exif 信息,在某些情况下会造成隐私泄露。

Java 操作 Exif

我们可通过 Apache Commons Imaging 来读取和写入 Exif 元数据。

  1. 引入依赖 commons-imaging

    <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-imaging</artifactId> <version>1.0-alpha1</version> </dependency>
  2. 读取 Exif

    final ImageMetadata metadata = Imaging.getMetadata(bytes); final JpegImageMetadata jpegMetadata = (JpegImageMetadata) metadata; final TiffImageMetadata exif = jpegMetadata.getExif();
  3. 写入 Exif

    final TiffOutputSet outputSet = exif.getOutputSet(); new ExifRewriter().updateExifMetadataLossless(bytes, os, outputSet);

Java 删除敏感 Exif

这里提供两种删除 Exif 信息的方法,第一种比较暴力,通过图片字节数组中 Exif 标识开头和结尾来完整擦除整个 Exif 段:

public static byte[] removeExif(final byte[] bytes) { try { final byte b0 = bytes[0]; final byte b1 = bytes[1]; if (-1 != b0 || -40 != b1) { // FF D8 return bytes; // not jpeg } if (-1 != bytes[2] || -31 != bytes[3]) { // exif seg: FF E1 return bytes; // no exif } String len0 = Integer.toHexString(bytes[4]); if (2 > len0.length()) { len0 = "0" + len0; } else { len0 = len0.substring(len0.length() - 2); } String len1 = Integer.toHexString(bytes[5]); if (2 > len1.length()) { len1 = "0" + len1; } else { len1 = len1.substring(len1.length() - 2); } final String lenStr = len0 + "" + len1; final int len = Integer.parseInt(lenStr, 16); final byte[] ret = new byte[bytes.length - len - 4 - 2]; ret[0] = -1; ret[1] = -40; System.arraycopy(bytes, 4 + len, ret, 2, ret.length - 2); return ret; } catch (final Exception e) { LOGGER.log(Level.ERROR, "Removes Exif failed", e); return bytes; } }

该方法的优点是性能很好,但缺点就是会擦除 Exif 中有用的信息(比如方向 Orientation,如果没有该字段,那么浏览器就无法自动旋转图片为正常方向了)。

另一个方法是通过上面介绍的 commons-imaging 来删除,这样可以保留想要的字段:

public static byte[] removeExif(final byte[] bytes) throws Exception { try (final ByteArrayOutputStream os = new ByteArrayOutputStream()) { TiffOutputSet outputSet = null; final ImageMetadata metadata = Imaging.getMetadata(bytes); if (!(metadata instanceof JpegImageMetadata)) { return bytes; } final JpegImageMetadata jpegMetadata = (JpegImageMetadata) metadata; final TiffImageMetadata exif = jpegMetadata.getExif(); if (null != exif) { outputSet = exif.getOutputSet(); } if (null == outputSet) { return bytes; } final List<TiffOutputDirectory> directories = outputSet.getDirectories(); for (final TiffOutputDirectory directory : directories) { final List<TiffOutputField> fields = directory.getFields(); for (final TiffOutputField field : fields) { if (!StringUtils.equalsIgnoreCase("Orientation", field.tagInfo.name)) { outputSet.removeField(field.tagInfo); } } } new ExifRewriter().updateExifMetadataLossless(bytes, os, outputSet); return os.toByteArray(); } }
  • Exif
    4 引用 • 13 回帖
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3194 引用 • 8214 回帖
  • 图片处理
    12 引用 • 34 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...