问题
Using imageIO, I usually have the problem of transforming an image file, and after overwriting it, it loses all of its EXIF data. Is there any way to preserve it without first extracting it, caching it, and then resetting it?
回答1:
ImageIO do have this functionality itself, but instead of ImageIO.read you will need to use ImageReader:
ImageReader reader = ImageIO.getImageReadersBySuffix("jpg").next();
(you may want to also check if such reader exists). Then you need to set the input:
reader.setInput(ImageIO.createImageInputStream(your_imput_stream));
Now you may save your metadata:
IIOMetadata metadata = reader.getImageMetadata(0);
// As far as I understand you should provide
// index as tiff images could have multiple pages
And then read the image:
BufferedImage bi = reader.read(0);
When you want to save new image, you should use 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<ImageWriter> 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);
As it's old topic, I guess this answer is a bit too late, but may help others as this topic is still googlable.
回答2:
Here is my solution using a combination of ImageIO, Imgscalr and Apache commons-imaging. It's a pity there's no single library which combines reading the image with its metadata, making this probably rather excessive on memory usage; Improvements welcome.
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;
}
}
来源:https://stackoverflow.com/questions/8972357/manipulate-an-image-without-deleting-its-exif-data