Android开发:如何通过字节数组写入EXIF数据?
问题原因
你猜的完全没错!ExifInterface(InputStream)这个构造函数是专门为读取EXIF数据设计的,内部根本没有实现写入逻辑——输入流本身是只读的,调用setAttribute后没有可写入的存储目标,所以会静默失败,后续自然读不到修改后的值。
原生解决方案:临时文件中转
虽然你的应用更倾向于直接操作字节数组,但Android原生ExifInterface要写入修改,必须通过文件路径构造实例(因为它需要可写入的存储载体)。我们可以用临时文件做中转,操作完成后再把文件转回字节数组,步骤如下:
// 1. 将原始JPEG字节数组写入临时文件 File tempExifFile = File.createTempFile("camera_exif_temp", ".jpg", getCacheDir()); try (FileOutputStream fos = new FileOutputStream(tempExifFile)) { fos.write(jpeg); } catch (IOException e) { Log.e(TAG, "Failed to write temp file", e); return; } // 2. 用文件路径构造可写入的ExifInterface ExifInterface exif = null; try { exif = new ExifInterface(tempExifFile.getAbsolutePath()); // 设置内置标签 exif.setAttribute(ExifInterface.TAG_ARTIST, "myself"); // 设置自定义标签(注意规范,见下文) exif.setAttribute("MY_OWN_TAG", "somevalue"); // 保存所有修改 exif.saveAttributes(); } catch (IOException e) { Log.e(TAG, "Failed to modify EXIF", e); } // 3. 将修改后的文件转回字节数组 byte[] modifiedJpeg = null; try (FileInputStream fis = new FileInputStream(tempExifFile); ByteArrayOutputStream baos = new ByteArrayOutputStream()) { byte[] buffer = new byte[1024]; int len; while ((len = fis.read(buffer)) != -1) { baos.write(buffer, 0, len); } modifiedJpeg = baos.toByteArray(); } catch (IOException e) { Log.e(TAG, "Failed to read modified file", e); } // 4. 清理临时文件(可选,缓存目录文件会被系统自动清理,手动删除更稳妥) if (tempExifFile.exists()) { tempExifFile.delete(); } // 现在modifiedJpeg就是带修改后EXIF的字节数组了
无文件的第三方库方案
如果完全不想碰文件,可以用第三方库直接在内存中操作JPEG的EXIF数据,比如Apache Commons Imaging(轻量级,无需依赖庞大的图片处理库):
首先添加依赖(在build.gradle中):
implementation 'org.apache.commons:commons-imaging:1.0-alpha3'
然后用库来修改EXIF:
try { // 读取原始JPEG的元数据 JpegImageMetadata metadata = (JpegImageMetadata) Imaging.getMetadata(jpeg); ExifRewriter rewriter = new ExifRewriter(); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); // 更新EXIF数据 rewriter.updateExifMetadataLossless(jpeg, outputStream, metadata -> { // 设置内置标签 metadata.getExif().setStringValue(ExifTagConstants.EXIF_TAG_ARTIST, "myself"); // 设置自定义标签(需要指定标签ID和数据类型,遵循EXIF规范) TagInfo customTag = new TagInfo("MY_OWN_TAG", 0x9999, TagDataType.STRING); metadata.getExif().setStringValue(customTag, "somevalue"); }); byte[] modifiedJpeg = outputStream.toByteArray(); // 使用修改后的字节数组 } catch (ImageReadException | IOException e) { Log.e(TAG, "Failed to modify EXIF via Commons Imaging", e); }
自定义标签的注意事项
直接用"MY_OWN_TAG"这种字符串作为标签名,可能无法被标准的EXIF读取工具识别,因为EXIF规范中标签是通过数值ID而非字符串标识的。如果需要兼容性:
- 优先使用EXIF标准预留的自定义字段,比如
ExifInterface.TAG_USER_COMMENT来存储自定义信息; - 如果必须用自己的标签,要为它分配一个符合规范的私有ID(通常是0x8000以上的数值),并确保数据类型正确(比如字符串、整数等)。
内容的提问来源于stack exchange,提问作者nm_tp
相关产品推荐
相关产品推荐

