2013-10-01 63 views
0

是否有可能通过逐字节地创建一个BufferedImage来防止BufferedImage抛出OutOfMemoryError异常?防止BufferedImage抛出OutMemoryError

我使用这个方法来裁剪图像:

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

} 

我有512M的一个MaxPermSize参数,如果有人上传16000x10000的图像,我会得到一个OutOfMemoryError:BufferedImage source = ImageIO.read(originalImage);

+0

PS:PermGen大小通常与您看到的OOME无关。图像数据分配在堆上,因此您需要增加堆大小(例如-Xmx512m)。 – haraldK

回答

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

如果总是只在图像的一个小区域有兴趣,你可以只加载一个区域(您可以预先计算出大概多少内存需要,以避免在最OOME保留一些记忆例如:

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

我需要一个完整的图像和缩略图 –

+0

为什么?你的问题中的代码总是在阅读后裁剪图像。这将产生与裁剪相同的结果,并且(至少50%)内存减少。 – haraldK

+0

因为我有一个画廊,我在其中展示剪裁的缩略图和显示完整图像的页面。 –