ZetCode

Java ImageIO

Last modified: July 17, 2026

This Java ImageIO tutorial shows how to read and write images using the javax.imageio package. We cover reading and writing images, format conversion, image scaling, working with thumbnails, using ImageReader and ImageWriter for advanced control, and reading image metadata.

Image I/O

The javax.imageio package provides a plug-in based framework for reading and writing images in Java. It supports a wide range of image formats through standard and third-party plug-ins. The central class is ImageIO, which offers static convenience methods for common operations.

The Java Image I/O API is organised around the following key abstractions:

Class / Interface Purpose
ImageIO Static convenience methods for reading, writing, and format detection
ImageReader Decodes images from a stream or file with fine-grained control
ImageWriter Encodes images to a stream or file with fine-grained control
ImageReadParam Specifies how a stream should be decoded (source region, subsampling, etc.)
ImageWriteParam Specifies how an image should be encoded (compression, tiling, etc.)
IIOImage Container for an image, optional thumbnails, and metadata
ImageTypeSpecifier Describes the colour model and sample model of an image

The standard Java runtime includes built-in plug-ins for BMP, GIF, JPEG, PNG, TIFF, and WBMP image formats.

Reading an image from a file

The simplest way to read an image is using the ImageIO.read(File) method, which returns a BufferedImage.

Main.java
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;

void main() {

    try {
        BufferedImage image = ImageIO.read(new File("image.png"));
        System.out.println("Image read successfully");
        System.out.printf("Width: %d, Height: %d%n",
                image.getWidth(), image.getHeight());
    } catch (IOException e) {
        System.err.println("Error reading image: " + e.getMessage());
    }
}

The ImageIO.read method automatically detects the image format by examining the file header. It returns a BufferedImage that can be used for further processing or display.

$ java Main.java
Image read successfully
Width: 800, Height: 600

Reading an image from an InputStream

Images can also be read from an InputStream, which is useful when the image data comes from a network source or a byte array.

Main.java
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import javax.imageio.ImageIO;

void main() {

    try {
        byte[] imageBytes = Files.readAllBytes(Path.of("image.png"));
        InputStream is = new ByteArrayInputStream(imageBytes);
        BufferedImage image = ImageIO.read(is);

        System.out.printf("Width: %d, Height: %d%n",
                image.getWidth(), image.getHeight());
    } catch (IOException e) {
        System.err.println("Error: " + e.getMessage());
    }
}

We read the image file into a byte array, wrap it in a ByteArrayInputStream, and pass it to ImageIO.read.

$ java Main.java
Width: 800, Height: 600

Writing an image to a file

The ImageIO.write method encodes a BufferedImage to the specified format and writes it to a file.

Main.java
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;

void main() {

    try {
        int width = 200;
        int height = 200;

        BufferedImage image = new BufferedImage(width, height,
                BufferedImage.TYPE_INT_RGB);

        // Fill with red
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                image.setRGB(x, y, 0xFF0000);
            }
        }

        File output = new File("red_square.png");
        ImageIO.write(image, "png", output);

        System.out.println("Image written to " + output.getPath());
    } catch (IOException e) {
        System.err.println("Failed to write image: " + e.getMessage());
    }
}

We create a 200x200 red square image programmatically and write it to a PNG file using ImageIO.write. The second parameter specifies the format name ("png", "jpg", "gif", "bmp", "tiff").

$ java Main.java
Image written to red_square.png

Format conversion

A common task is converting an image from one format to another. This is easily done by reading the image with one format and writing it with another.

Main.java
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;

void main() {

    try {
        BufferedImage image = ImageIO.read(new File("photo.jpg"));

        File output = new File("photo.png");
        ImageIO.write(image, "png", output);

        System.out.println("Converted JPG to PNG successfully");
    } catch (IOException e) {
        System.err.println("Error: " + e.getMessage());
    }
}

We read a JPEG file and write it as a PNG. The Image I/O API handles all necessary transcoding internally. This approach works for any pair of supported formats.

$ java Main.java
Converted JPG to PNG successfully

Scaling an image

When reading an image, we can scale it to a desired dimension. The following example reads an image and scales it to a fixed width while preserving the aspect ratio.

Main.java
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;

void main() {

    try {
        BufferedImage source = ImageIO.read(new File("photo.jpg"));

        if (source == null) {
            System.err.println("Failed to read image. Unsupported format?");
            return;
        }

        int targetWidth = 300;
        int targetHeight = (int) ((double) source.getHeight()
                / source.getWidth() * targetWidth);

        BufferedImage scaled = new BufferedImage(targetWidth, targetHeight,
                BufferedImage.TYPE_INT_RGB);

        Graphics2D g2d = scaled.createGraphics();
        try {
            g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                    RenderingHints.VALUE_INTERPOLATION_BILINEAR);
            g2d.drawImage(source, 0, 0, targetWidth, targetHeight, null);
        } finally {
            g2d.dispose();
        }

        ImageIO.write(scaled, "jpg", new File("photo_scaled.jpg"));
        System.out.println("Scaled image written successfully");
    } catch (IOException e) {
        System.err.println("Error: " + e.getMessage());
    }
}

We calculate the target height to preserve the aspect ratio based on the target width. The Graphics2D.drawImage method performs the scaling with bilinear interpolation for smooth results.

The RenderingHints.KEY_INTERPOLATION hint controls how pixels are calculated during scaling:

Writing with compression level

For formats that support compression (such as JPEG), we can control the compression quality using ImageWriteParam.

Main.java
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;

void main() {

    try {
        BufferedImage image = ImageIO.read(new File("photo.jpg"));

        Iterator<ImageWriter> writers =
                ImageIO.getImageWritersByFormatName("jpg");

        if (!writers.hasNext()) {
            System.err.println("No writer found");
            return;
        }

        ImageWriter writer = writers.next();
        try {
            ImageWriteParam param = writer.getDefaultWriteParam();
            param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
            param.setCompressionQuality(0.3f);

            File output = new File("photo_low.jpg");
            try (ImageOutputStream ios = ImageIO.createImageOutputStream(output)) {
                writer.setOutput(ios);
                writer.write(null, new IIOImage(image, null, null), param);
            }

            System.out.println("Written with compression quality 0.3");
        } finally {
            writer.dispose();
        }
    } catch (IOException e) {
        System.err.println("Error: " + e.getMessage());
    }
}

We obtain an ImageWriter for the JPEG format, configure the write param with explicit compression mode and a quality setting of 0.3 (lower means smaller file size but lower quality), and write the image.

$ java Main.java
Written with compression quality 0.3

Using ImageReader for fine-grained control

For advanced reading operations, such as reading a specific region or subsampling, we can use ImageReader directly.

Main.java
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;

void main() {

    try {
        File file = new File("photo.jpg");

        try (ImageInputStream iis = ImageIO.createImageInputStream(file)) {

            Iterator<ImageReader> readers =
                    ImageIO.getImageReaders(iis);

            if (!readers.hasNext()) {
                System.err.println("No reader found");
                return;
            }

            ImageReader reader = readers.next();
            try {
                reader.setInput(iis);

                // Read only the top-left 200x200 region
                ImageReadParam param = reader.getDefaultReadParam();
                param.setSourceRegion(new Rectangle(0, 0, 200, 200));

                BufferedImage region = reader.read(0, param);
                ImageIO.write(region, "jpg", new File("region.jpg"));

                System.out.printf("Region image: %dx%d%n",
                        region.getWidth(), region.getHeight());
            } finally {
                reader.dispose();
            }
        }
    } catch (IOException e) {
        System.err.println("Error: " + e.getMessage());
    }
}

We use ImageReader to read only a 200x200 region from the top-left corner of the image, without loading the entire image into memory. The setSourceRegion method on ImageReadParam controls which portion of the image is decoded.

$ java Main.java
Region image: 200x200

Reading image thumbnails

Some image formats (such as JPEG) can embed thumbnail previews. The ImageReader can read these thumbnails without decoding the full image.

Main.java
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;

void main() {

    try {
        File file = new File("photo.jpg");

        try (ImageInputStream iis = ImageIO.createImageInputStream(file)) {

            Iterator<ImageReader> readers =
                    ImageIO.getImageReaders(iis);

            if (!readers.hasNext()) {
                System.err.println("No reader found");
                return;
            }

            ImageReader reader = readers.next();
            try {
                reader.setInput(iis);

                int numThumbs = reader.getNumThumbnails(0);
                System.out.printf("Number of thumbnails: %d%n", numThumbs);

                if (numThumbs > 0) {
                    BufferedImage thumb = reader.readThumbnail(0, 0);
                    System.out.printf("Thumbnail size: %dx%d%n",
                            thumb.getWidth(), thumb.getHeight());
                }
            } finally {
                reader.dispose();
            }
        }
    } catch (IOException e) {
        System.err.println("Error: " + e.getMessage());
    }
}

The getNumThumbnails method returns the number of embedded thumbnails for the given image index. readThumbnail reads a specific thumbnail without decoding the full-resolution image.

$ java Main.java
Number of thumbnails: 1
Thumbnail size: 160x120

Reading image metadata

Image metadata contains information such as the image dimensions, colour space, compression type, and format-specific details (like EXIF data in JPEG).

Main.java
void main() {
    File file = new File("photo.jpg");

    try (ImageInputStream iis = ImageIO.createImageInputStream(file)) {

        Iterator<ImageReader> readers = ImageIO.getImageReaders(iis);
        if (!readers.hasNext()) {
            System.err.println("No reader found");
            return;
        }

        ImageReader reader = readers.next();
        try {
            reader.setInput(iis);
            IIOMetadata metadata = reader.getImageMetadata(0);
            System.out.println("Available metadata formats:");
            for (String name : metadata.getMetadataFormatNames()) {
                System.out.println("  " + name);
            }
        } finally {
            reader.dispose();
        }

    } catch (IOException e) {
        System.err.println("Error: " + e.getMessage());
    }
}

The getImageMetadata method returns an IIOMetadata object containing format-specific metadata for the given image index. The getMetadataFormatNames method lists the available metadata representation formats (e.g., standard, native).

$ java Main.java
Available metadata formats:
  javax_imageio_jpeg_image_1.0
  javax_imageio_1.0

Listing available readers and writers

The ImageIO class provides methods to discover which image format plug-ins are available in the current runtime.

Main.java
import java.util.Arrays;
import javax.imageio.ImageIO;

void main() {

    String[] readerFormats = ImageIO.getReaderFormatNames();
    String[] writerFormats = ImageIO.getWriterFormatNames();

    System.out.println("Supported read formats:");
    System.out.println("  " + Arrays.toString(readerFormats));

    System.out.println("Supported write formats:");
    System.out.println("  " + Arrays.toString(writerFormats));

    System.out.println();
    System.out.printf("Number of read formats: %d%n",
            readerFormats.length);
    System.out.printf("Number of write formats: %d%n",
            writerFormats.length);
}

getReaderFormatNames and getWriterFormatNames return the format names (e.g., "jpg", "png") that the current runtime supports for reading and writing.

$ java Main.java 
Supported read formats:
  [JPG, jpg, tiff, bmp, BMP, gif, GIF, WBMP, png, PNG, JPEG, tif, TIF, TIFF, wbmp, jpeg]
Supported write formats:
  [JPG, jpg, tiff, bmp, BMP, gif, GIF, WBMP, png, PNG, JPEG, tif, TIF, TIFF, wbmp, jpeg]

Number of read formats: 16
Number of write formats: 16

Source

Java ImageIO - Language Reference

In this article, we have worked with the Java Image I/O API.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Java tutorials.