操作图像而不删除其EXIF数据

使用imageIO,我通常会遇到转换图像文件的问题,在覆盖它之后,它会丢失所有的EXIF数据。 有没有办法保留它而不首先提取它,缓存它,然后重置它?

这是我使用ImageIO,Imgscalr和Apache commons-imaging组合的解决方案。 遗憾的是,没有一个单独的库将图像与元数据相结合,这使得内存使用可能相当过分; 改进欢迎。

import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import javax.imageio.ImageIO; import org.apache.commons.imaging.ImageReadException; import org.apache.commons.imaging.ImageWriteException; import org.apache.commons.imaging.Imaging; import org.apache.commons.imaging.common.IImageMetadata; import org.apache.commons.imaging.formats.jpeg.JpegImageMetadata; import org.apache.commons.imaging.formats.jpeg.exif.ExifRewriter; import org.apache.commons.imaging.formats.tiff.TiffImageMetadata; import org.apache.commons.io.IOUtils; import org.imgscalr.Scalr; public class ImageData { private byte[] imageData; public ImageData(InputStream instream) throws IOException { imageData = IOUtils.toByteArray(instream); instream.close(); } public synchronized void resize(int maxDimension) throws IOException, ImageReadException, ImageWriteException { // Resize the image if necessary BufferedImage image = readImage(imageData); if (image.getWidth() > maxDimension || image.getHeight() > maxDimension) { // Save existing metadata, if any TiffImageMetadata metadata = readExifMetadata(imageData); imageData = null; // allow immediate GC // resize image = Scalr.resize(image, maxDimension); // rewrite resized image as byte[] byte[] resizedData = writeJPEG(image); image = null; // allow immediate GC // Re-code resizedData + metadata to imageData if (metadata != null) { this.imageData = writeExifMetadata(metadata, resizedData); } else { this.imageData = resizedData; } } } private TiffImageMetadata readExifMetadata(byte[] jpegData) throws ImageReadException, IOException { IImageMetadata imageMetadata = Imaging.getMetadata(jpegData); if (imageMetadata == null) { return null; } JpegImageMetadata jpegMetadata = (JpegImageMetadata)imageMetadata; TiffImageMetadata exif = jpegMetadata.getExif(); if (exif == null) { return null; } return exif; } private byte[] writeExifMetadata(TiffImageMetadata metadata, byte[] jpegData) throws ImageReadException, ImageWriteException, IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); new ExifRewriter().updateExifMetadataLossless(jpegData, out, metadata.getOutputSet()); out.close(); return out.toByteArray(); } private BufferedImage readImage(byte[] data) throws IOException { return ImageIO.read(new ByteArrayInputStream(data)); } private byte[] writeJPEG(BufferedImage image) throws IOException { ByteArrayOutputStream jpegOut = new ByteArrayOutputStream(); ImageIO.write(image, "JPEG", jpegOut); jpegOut.close(); return jpegOut.toByteArray(); } public synchronized void writeJPEG(OutputStream outstream) throws IOException { IOUtils.write(imageData, outstream); } public synchronized byte[] getJPEGData() { return imageData; } } 

ImageIO本身具有此function,但您需要使用ImageReader而不是ImageIO.read

 ImageReader reader = ImageIO.getImageReadersBySuffix("jpg").next(); 

(您可能还想检查这样的阅读器是否存在)。 然后你需要设置输入:

 reader.setInput(ImageIO.createImageInputStream(your_imput_stream)); 

现在您可以保存元数据:

 IIOMetadata metadata = reader.getImageMetadata(0); // As far as I understand you should provide // index as tiff images could have multiple pages 

然后阅读图片:

 BufferedImage bi = reader.read(0); 

如果要保存新图像,则应使用ImageWriter:

 // I'm writing to byte array in memory, but you may use any other stream ByteArrayOutputStream os = new ByteArrayOutputStream(255); ImageOutputStream ios = ImageIO.createImageOutputStream(os); Iterator iter = ImageIO.getImageWritersByFormatName("jpeg"); ImageWriter writer = iter.next(); writer.setOutput(ios); //You may want also to alter jpeg quality ImageWriteParam iwParam = writer.getDefaultWriteParam(); iwParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); iwParam.setCompressionQuality(.95f); //Note: we're using metadata we've already saved. writer.write(null, new IIOImage(bi, null, metadata), iwParam); writer.dispose(); ImageIO.write(bi, "jpg", ios); 

由于这是一个古老的话题,我猜这个答案有点太晚了,但可能有助于其他人,因为这个话题仍然可以转让。

Interesting Posts