防止BufferedImage抛出OutOfMemoryError

时间:2013-10-01 00:53:08

标签: java out-of-memory bufferedimage

是否可以通过逐字节创建BufferedImage来阻止BufferedImage抛出OutOfMemoryError异常?

我正在使用此方法裁剪图片:

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

}

我的MaxPermSize为512m,如果有人上传了16000x10000图像,我会收到一个OutOfMemoryError:BufferedImage source = ImageIO.read(originalImage);

2 个答案:

答案 0 :(得分:0)

这是我的自我解决方案。 现在,当文件上传时,我将检查它是否是图像,获取图像大小而不读取所有文件并检查它是否太大。

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

答案 1 :(得分:0)

如果你总是只对图像的一个小区域感兴趣,你可以通过只加载那个区域来保留一些内存(你可以预先计算大约需要多少内存,以避免在大多数情况下使用OOME): / p>

// 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...