Site Network: Personal | Professional | Photography

Technical Blog

This blog will contain content related to Java, Seam, Security, my sites and projects, as well as other technical subjects I am interested in.

Comments and questions are welcome!

How To Resize Uploaded Images Using Java

I am building a Seam application which need to support users uploading images, and then the site displaying them. I wanted to resize big images into something reasonable (say 1024 or 800 px wide) and generate a thumbnail, and I also wanted to standardize on a single image format just to make things easy and consistent.

So I needed method that would take an uploaded image as a byte array (byte[]) and would resize the image (if needed) and convert it to a fixed quality JPG, and give me back a byte array to store in the database. I am using Seam, so I get a handy byte array, but this method should work fine for non-Seam applications as well.

 
    /**
     * This method takes in an image as a byte array (currently supports GIF, JPG, PNG and possibly other formats) and
     * resizes it to have a width no greater than the pMaxWidth parameter in pixels. It converts the image to a standard
     * quality JPG and returns the byte array of that JPG image.
     *
     * @param pImageData
     *                the image data.
     * @param pMaxWidth
     *                the max width in pixels, 0 means do not scale.
     * @return the resized JPG image.
     * @throws IOException
     *                 if the iamge could not be manipulated correctly.
     */
    public byte[] resizeImageAsJPG(byte[] pImageData, int pMaxWidth) throws IOException {
	// Create an ImageIcon from the image data
	ImageIcon imageIcon = new ImageIcon(pImageData);
	int width = imageIcon.getIconWidth();
	int height = imageIcon.getIconHeight();
	mLog.info("imageIcon width: #0  height: #1", width, height);
	// If the image is larger than the max width, we need to resize it
	if (pMaxWidth > 0 && width > pMaxWidth) {
	    // Determine the shrink ratio
	    double ratio = (double) pMaxWidth / imageIcon.getIconWidth();
	    mLog.info("resize ratio: #0", ratio);
	    height = (int) (imageIcon.getIconHeight() * ratio);
	    width = pMaxWidth;
	    mLog.info("imageIcon post scale width: #0  height: #1", width, height);
	}
	// Create a new empty image buffer to "draw" the resized image into
	BufferedImage bufferedResizedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
	// Create a Graphics object to do the "drawing"
	Graphics2D g2d = bufferedResizedImage.createGraphics();
	g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
	// Draw the resized image
	g2d.drawImage(imageIcon.getImage(), 0, 0, width, height, null);
	g2d.dispose();
	// Now our buffered image is ready
	// Encode it as a JPEG
	ByteArrayOutputStream encoderOutputStream = new ByteArrayOutputStream();
	JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(encoderOutputStream);
	encoder.encode(bufferedResizedImage);
	byte[] resizedImageByteArray = encoderOutputStream.toByteArray();
	return resizedImageByteArray;
    }
 

In my application I call this method twice, once to convert the uploaded image into a limited size JPG, and then once again to generate a much smaller thumbnail. I store both of these in the database and will use caching at the Apache layer to ensure performance.

17 Responses to “How To Resize Uploaded Images Using Java”

  1. Matt Sidesinger Says:

    I have gotten better quality in the past when creating the BufferedImage with the following logic to perform the resize:

    import java.awt.RenderingHints;
    import javax.media.jai.RenderedOp;
    import javax.media.jai.operator.SubsampleAverageDescriptor;
    ...
    RenderingHints hints = new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
    RenderedOp resizeOp = SubsampleAverageDescriptor.create(image, scale, scale, hints);
    BufferedImage bufferedResizedImage = resizeOp.getAsBufferedImage();
    
  2. Devon Says:

    Thank Matt, I will give that a try!

    – edit:

    How do you convert an incoming ImageIcon or Image to a RenderedImage needed for the SubsampleAverageDescriptor.create() call? I can see how to do it if I go through the process of painting it full size into a another BufferedImage, but that seems like it would make this call at least 2X as computationally expensive..

  3. Devon Says:

    Or did you mean just use the rendering hints you have there, and leave the rest of the code alone? Does the SubsampleAverageDescriptor provide superior resizing?

  4. Matt Sidesinger Says:

    Sorry about the not including anything about how to get a RenderedImage as I am not using an ImageIcon. A BufferedImage implements RenderedImage. You can creaete a BufferedImage from a byte[] by creating a ByteArrayInputStream and passing it to ImageIO.read().

    This post shows visual results of resizing with and without using SubsampleAverage: https://www.i-proving.ca/space/Technologies/Java+Advanced+Imaging

    I was experiencing similar results where edges were not smooth.

  5. How To Resize Uploaded Images Using Java - Better Way | Devon Hillard Tech Blog Says:

    […] How To Resize Uploaded Images Using Java […]

  6. vpetreski Says:

    http://www.comesolvego.com/2008/04/29/resize-images-with-java-high-quality-and-working-solution/

  7. Doug Says:

    I was wondering if anyone else was getting weird behavior doing this sort of thing on Mac OSX. I am using the code above to create a thumbnail and when the code is executed the org.apache.catalina.startup.Bootstrap process shows up on the menu bar at the top of my screen. This occurs on both Tiger and Leapord. On Tiger you can shutdown the machine and the machine stops the apache process and shuts down. On Leapord the machine does not shut down until the user chooses to quit org.apache.catalina.startup.Bootstrap manually. I really need to be able to size images but I can not expect my user base to manually quit org.apache.catalina.startup.Bootstrap before shutting down their machine.
    Has anyone else seen this behavior? By the way, it works fine on Windows.

    Thanks in advance,
    Doug

  8. Devon Says:

    @Doug:

    my production system is Linux, so it isn’t a big deal for me. I have noticed that on my dev box (OS X Leopard), JBoss will create a JBoss Main process with a menu, which sounds similar. When I shutdown JBoss though, that process also dies. I haven’t looked into it much however.

    If you find out how to prevent it, I’d love to know.

    Thanks!

    Devon

  9. Doug Says:

    On the upside at least I am not alone. On the down side…no solution…yet…..

  10. Doug Says:

    Hi Devon, I was wondering if you could perhaps help me on this problem. I have tried two different ways of sizing the image and have tracked it down to the following lines of code (from 2 different methods).

    First Way : ImageIcon imageIcon = new ImageIcon( pImageData ); //Where pImageData is the byte[]. When this line of code is executed the org.apache.catalina.startup.Bootstrap pops up on the upper right.

    Second way : Image image = Toolkit.getDefaultToolkit().createImage( pImageData );//Where pImageData is the byte[].
    When this line of code is executed the org.apache.catalina.startup.Bootstrap pops up on the upper right.

    Do you know of other ways of accomplishing this? The first example is right from this post. Any help would be GREATLY APPRECIATED!!!! I am at the point where I will try anything just to solve the problem.

    Thanks,
    Doug

  11. Devon Says:

    @Doug:

    Try adding this to your start script:

    export JAVA_OPTS=”-Djava.awt.headless=true”

    Let me know if that helps.

  12. Doug Says:

    It WORKS! You rock Devon!

    Thank you very much for your help I appreciate it.

  13. Devon Says:

    @Doug:

    No problem! I’m glad it worked!

  14. Doug Says:

    One last question please! What is the proper sytax for the following…it doesn’t seem to work when I want to put 2 parms on the JAVA_OPTS.
    I was setting just the memory and now I need the headless part as well. Can you tell me what is wrong with my JAVA_OPTS below?

    # JAVA_OPTS=”-Xms128M -Xmx512M”
    JAVA_OPTS=”-Djava.awt.headless=true;-Xms128M -Xmx512M”

    Thanks Again,
    Doug

  15. Devon Says:

    @Doug:

    Just use spaces:

    JAVA_OPTS=”-Djava.awt.headless=true -Xms128M -Xmx512M”

  16. Doug Says:

    Thanks!

  17. joaquin Says:

    it works! i tried it and voila

    tks

Leave a Reply