简体   繁体   中英

Preventing BufferedImage To Throw OutOfMemoryError

Is it possible prevent BufferedImage to throw OutOfMemoryError exceptions by creating byte by byte a BufferedImage?

I am using this method to crop images:

public static void cropImage(File originalImage, File to, int x1, int y1, int x2, int y2) {
    try {
        BufferedImage source = ImageIO.read(originalImage);

        String mimeType = "image/jpeg";
        if (to.getName().endsWith(".png")) {
            mimeType = "image/png";
        }
        if (to.getName().endsWith(".gif")) {
            mimeType = "image/gif";
        }
        int width = x2 - x1;
        int height = y2 - y1;

        // out
        BufferedImage dest = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Image croppedImage = source.getSubimage(x1, y1, width, height);
        Graphics graphics = dest.getGraphics();
        graphics.setColor(Color.WHITE);
        graphics.fillRect(0, 0, width, height);
        graphics.drawImage(croppedImage, 0, 0, null);
        ImageWriter writer = ImageIO.getImageWritersByMIMEType(mimeType).next();
        ImageWriteParam params = writer.getDefaultWriteParam();
        writer.setOutput(new FileImageOutputStream(to));
        IIOImage image = new IIOImage(dest, null, null);
        writer.write(null, image, params);
        writer.dispose();
        source = null;
    } catch (Exception e) {
        throw new RuntimeException(e);
    }

}

I have a MaxPermSize of 512m and if someone upload a 16000x10000 image I will get an OutOfMemoryError on: BufferedImage source = ImageIO.read(originalImage);

Here my self-solution. Now when a file is uploaded I will check if it is an image, get the image size without read the all file and check if it is too big.

public static boolean isImage(String fileName){
    Pattern pattern = Pattern.compile("([^\\s]+(\\.(?i)(png|jpg|jpeg|gif|bmp))$)");
    Matcher matcher = pattern.matcher(fileName);
    return matcher.matches();
}

public static Dimension getImageDimension(File file){
    ImageInputStream in = null;
    try{
        in = ImageIO.createImageInputStream(file);
        final Iterator<ImageReader> readers = ImageIO.getImageReaders(in);
        if (readers.hasNext()) {
            ImageReader reader = readers.next();
            try {
                reader.setInput(in);
                return new Dimension(reader.getWidth(0), reader.getHeight(0));
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                reader.dispose();
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (in != null) try {
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    return null;
}

public static boolean isImageTooBig(File file){
    boolean isImage = isImage(file.getName());
    if(!isImage) return false;
    Dimension dim = getImageDimension(file);
    int maxW = Integer.parseInt((String) Play.configuration.get("app.upload.image.maxW"));
    int maxH = Integer.parseInt((String) Play.configuration.get("app.upload.image.maxH"));
    if(dim.getWidth() > maxW) return true;
    if(dim.getHeight() > maxH) return true;
    return false;
}

If you are always only interested in a small region of the image, you can preserve some memory by only loading that region (you can calculate in advance roughly how much memory that will need, to avoid OOME in most cases):

// Same inputs as your original code

ImageInputStream in = ImageIO.createImageInputStream(originalImage);
BufferedImage source;

try {
    Iterator<ImageReader> readers = ImageIO.getImageReaders(in);

    if (readers.hasNext()) {
        ImageReader reader = readers.next();

        try {
            reader.setInput(in);
            ImageReadParam param = reader.getDefaultReadParam();
            param.setSourceRegion(x1, y1, width, height);

            // Source will be roughly width * height * bytes per pixel
            source = reader.read(0, param); 
        } 
        finally {
            reader.dispose();
        }
    }
} 
finally {
    in.close();
}

// Use your old code to store source...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM