2012-01-23 20 views
10

Al usar imageIO, suelo tener el problema de transformar un archivo de imagen y, después de sobreescribirlo, pierde todos sus datos EXIF. ¿Hay alguna forma de preservarlo sin primero extraerlo, almacenarlo en caché y luego reiniciarlo?Manipular una imagen sin eliminar sus datos EXIF ​​

+2

¿Almacenar en otro lugar y luego sobrescribir una nueva imagen con un viejo exif meta? http://www.screaming-penguin.com/node/7485 –

+0

Esto es precisamente lo que quiero evitar – preslavrachev

+2

¿cuál es el problema con copiar el meta? aquí hay otro ejemplo http://nucleussystems.com/blog/java-copy-exif-data –

Respuesta

9

Aquí está mi solución usando una combinación de ImageIO, Imgscalr y Apache commons-imaging. Es una lástima que no haya una única biblioteca que combine la lectura de la imagen con sus metadatos, lo que probablemente sea bastante excesivo para el uso de la memoria; Mejoras bienvenidas.

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; 
    } 

} 
+0

Muchas gracias. Funcionó bien. Lo único es que aparentemente 'IImageMetadata' se llama 'ImageMetadata' en el repositorio actual de Apache Commons Imaging –

+0

También verifique la otra solución de @Rigeborod que se ve un poco más eficiente –

8

ImageIO tienen esta funcionalidad en sí, pero en lugar de ImageIO.read tendrá que utilizar ImageReader:

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

(es posible que desee comprobar también si existe tal lector) . Luego hay que ajustar la entrada:

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

Ahora puede guardar los metadatos:

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

Y luego leer la imagen:

BufferedImage bi = reader.read(0); 

Cuando desea guardar una imagen nueva , debe utilizar 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); 

Como es un tema antiguo, creo que esta respuesta es un poco tarde, pero puede ayudar a otros ya que este tema todavía es googleable.

+0

Esto se ve sustancialmente más eficiente en la memoria que mi solución, Supongo que una copia de la imagen todavía está en la memoria, pero realmente no puede evitar eso. –

Cuestiones relacionadas