2013-10-01 62 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

因爲我有一個畫廊,我在其中展示剪裁的縮略圖和顯示完整圖像的頁面。 –