简单的说,就是要实现这样一个功能:把一个大图片(3638*2736)的JPEG 图片加载进来,按比例缩小后输出!在实现过程中,发现这样一个问题:在用 ImageIO.read(File input) 加载图片的时候,只要图片很大,就会报内存溢出;在网上了解了一下,似乎都有这个问题,很多人推荐修改JVM的内存,但个人觉得这个方法也有不妥,因为我不知道我要处理的图片到底要多大,1M?5M?100M?还有网友推荐,在用 ImageIO 的时候进行设置,如 ImageIO.setCacheDirectory(File   cacheDirectory)给它设定一个缓存路径 或者 ImageIO.setUseCache(false)用系统缓存,这两个方法测试了,似乎没有用!Toolkit 这个工具包里也有一个加载图片的方法,但是经过测试,这个方法加载的图片,在进行 JPEG 编码后,输出的图片是一个全黑的空图片。File _file = new File("d:\\1.jpg"); //读入文件 
Image src = javax.imageio.ImageIO.read(_file); //构造Image对象 
int wideth=src.getWidth(null); //得到源图宽 
int height=src.getHeight(null); //得到源图长 
BufferedImage tag = new BufferedImage(wideth/2,height/2,BufferedImage.TYPE_INT_RGB); 
tag.getGraphics().drawImage(src,0,0,wideth/2,height/2,null); //绘制缩小后的图 
FileOutputStream out=new FileOutputStream("D:\\newfile.jpg"); //输出到文件流 
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
encoder.encode(tag); //近JPEG编码 
out.close(); 
 以上这个方法,在处理相对较小的图片,比如1M左右的图片,是可以的,但是如果 图片较大,在Image src = javax.imageio.ImageIO.read(_file); 就抛内存溢出;
如果这里的Image src 用 Toolkit来加载图片获得,在后面 进行 JPEG 编码后,输出的图片是全黑的空图片!
有没有好的解决办法,请有经验的高手指点下!谢谢!

解决方案 »

  1.   

    为什么不动手自己写一个ToolClass来做这件事情?
      

  2.   

    为什么不动手自己写一个ToolClass来做这件事情?
      

  3.   

    从sun网站来看,这是一个sun的jdk一个bug,你需要使用jdk1.5来解决这个问题,原文:http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6247845
      

  4.   


    看了SUN网站的这个BUG描述,不过似乎不是一个问题,上面讲述的是ImageIO.write产生的问题,“Demonstrate memory leak when exception is thrown up through ImageIO.write.”我把这个BUG描述中的例子贴下来测试了,还是那个问题,在 BufferedImage original = ImageIO.read(originalStream); 就产生了内存溢出的异常!
      

  5.   

    我遇到过类似的问题 1.5甚至1.6都没有解决
    比较好的方法是自己实现IamgeReader,用文件通道的读取图像数据,然后分块处理数据文件。
    当然我没有亲自实现 到底行不行就不知道了。
      

  6.   

    Toolkit加载是异步的,它有一个观察器,你要等待它回加载完成才能再draw出去
    所以在一个循环中判断:boolean flag = false;
    while(!(flag = tag.getGraphics().drawImage(src,0,0,wideth/2,height/2,null))
    {
      try{Thread.sleep(10);}catch(Exception e){}
    }
      

  7.   

    1 增加你的内存使用最大量,一般设置到512M
    2 修改你的算法,如果你必须把所有图形全部读取才能操作,那么大于500M的图片怎么办?500G的图片呢?
      

  8.   

    一个个说得轻巧,站着说话不腰疼.有本事自己写看看.要自己实现,首先要分析jpeg文件格式,且不说95%的人写不出来.
    即使那5%的人能分析jpeg的格式,读取文件本身不成问题,但要把jpeg的数据转换成Image对象或BufferedImage对象,相当地VC中的bitmap.
    还是要把原始jpeg的数据一次提交给底层的GDI接口才能转换.然后底层接口返回一个BitMap的字节数组.我们知压缩格式的图片数据被加载成
    Image后其实被还原成了Bitmap,数据比原始的增加好多倍,你们有本事给我写一个看看,我给你512M内存的jvm,你给我读100m的jpeg图片.
      

  9.   

    已经可以处理10M的JPEG图片,明后天抽空整理出来,给有兴趣的朋友!不过同时发现一个问题,没有解决,不知道有没有可能解决,就是JPEG编码压缩出来的图片质量不怎么好,这里我所说的不怎么好,是跟用PS压缩出来的同样分辨率的图片比较,没有PS出来的清晰!这个应该跟Java对JPEG编码格式的支持有关系,hoho,谁能自己动手重写,麻烦教教我!哈哈
      

  10.   

    本着负责任的态度,来一个总结,给有需要的朋友,一点浅薄的......首先感谢一下 axman 江渚渔樵 ,给我的提示和启发很大!谢谢!到现在我还认为,ImageIO.read(File input) 的这个方法,不说它有BUG,但至少有问题,没有办法加载一个大于1M+ 的JPEG图片(具体多大不能加载了,不清楚)!先把代码贴上再废话吧!
    private void compressImageFile() {
    JFileChooser fileChooser = new JFileChooser("d://");
    fileChooser.showOpenDialog(this);
    File file = fileChooser.getSelectedFile(); if (null != file && file.exists()) {
    Toolkit toolkit = Toolkit.getDefaultToolkit();
    Image srcImage = toolkit.getImage(file.getAbsolutePath()); // 构造Image对象 int wideth = -1;
    int height = -1;
    boolean flag = true;
    while (flag) {
    wideth = srcImage.getWidth(null); // 得到源图宽
    height = srcImage.getHeight(null); // 得到源图长
    System.out.println("wideth:" + wideth + " height:" + height);
    if (wideth > 0 && height > 0) { // 因为 Toolkit.getImage 是异步读取,如果
    // wideth 和 height
    // 都大于0,表明图片已经加载完毕
    // imageCanvas.setImage(srcImage);
    flag = false;
    } else {
    try {
    Thread.sleep(10);
    } catch (Exception e) {
    e.printStackTrace();
    }
    }
    } // 加载完毕,输出
    int w = 1024;
    float s = (float) wideth / 1024.0f;
    int h = (int) ((float) height / s);
    BufferedImage bufferedImage = new BufferedImage(w, h,
    BufferedImage.TYPE_INT_RGB);
    // bufferedImage.getGraphics().drawImage(srcImage, 0, 0, 1024,
    // 768, null); // 绘制缩小后的图
    boolean flag2 = false;
    while (!(flag2 = bufferedImage.getGraphics().drawImage(srcImage, 0,
    0, w, h, this))) {
    try {
    Thread.sleep(10);
    } catch (Exception e) {
    e.printStackTrace();
    }
    } try { File outputFile = new File("d://hhh.jpg");
    if (!outputFile.exists()) {
    outputFile.createNewFile();
    }
    FileOutputStream out = new FileOutputStream(outputFile); // 输出到文件流
    JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
    encoder.encode(bufferedImage); // 近JPEG编码 out.close();
    } catch (Exception e) {
    e.printStackTrace();
    }
    }
    }
    本人对 图形图像 这块一点不懂的,只是借用现成的API来写个小工具,自己用用而已!
    上面这段代码,只是对一个图片的处理,既然是小工具,简单易用才是最终目标,设定一个目录,把该目录下的所有图片,遍历来处理,呵呵,有兴趣的朋友自己实现一下吧!其实到这里,还是有很多问题存在,关于JPEG图片压缩的问题,我已经说了,我一点不懂的!
    有这样一个例子,从网上下载的一张上海地图,大小是6050*5100,容量8M多,感觉这个图片就是一个被压缩过的JPEG图片,把这个图片用上面的这段代码来处理,直接就内存溢出了!然后我用 PS 打开这个图片,改变图片大小,在以JPEG格式输入,发现,图像大小为4000*3372,容量就达11.9M,超过了原文件!
    所以我感觉,这个原文件有压缩的!
    我用这个8M的原文件,通过 PS,输出1M-20M不等的 JPEG图片文件,用上面的这个代码来处理,可以处理10M+的图片了,有这样一组数据:JVM内存使用情况测试:System.out.println("total_memory=" + Runtime.getRuntime().totalMemory()/1024/1024 + "M");    
    System.out.println("max_memory=" + Runtime.getRuntime().maxMemory()/1024/1024 + "M");total_memory=1M
    max_memory=63M内存初始值大小  加载的文件大小  内存占用大小
    17420k 1.1M 27528k
    17420k 1.5M 28828k
    17420k 2.3M 31596k
    17476k 3.1M 34960k
    17420k 3.7M 37428k
    17452k 4.4M 40240k
    17472k 5.4M 44864k
    17436k 6.2M 48316k
    17420k 7.3M 54016k
    17424k 8.1M 58076k
    17448k 9.5M 64792k
    17468k 11.9M 77244k
    17460k 17.3M OutOfMemory还有一个问题,同样一个图片文件,用PS处理出来的和用上面这个代码处理出来的同样分辨率的图片,起容量和质量有差距的,是用PS处理出来的较清晰一点,当然容量自然也大了;
    这个问题,我想跟JAVA对JPEG的支持有关系了,主张自己写低层代码,能实现优化的,我首先向你表示敬佩,要是有成果,还望分享一下啊,哈哈!为了琢磨这个问题,这两天占用了不少工作时间,哈哈,良心不安,开始努力工作了!哈哈!