在只知道压缩版本的大小时,防止将大图像加载到“BufferedImage”中

问题描述 投票:0回答:2

我有一项服务可以接收已上传图片的 URL:它的任务是调整它们的大小并压缩保存,像这样加载它们:

BufferedImage src = ImageIO.read(imgURL);

虽然可能存在问题......要压缩这些图像,我需要先将它们加载到内存中,但我无法仅通过查看报告的大小来判断它们将占用多少 RAM:80 KB PNG 可以为 5000px * 5000px,对于 300 KB JPEG 也可能发生同样的情况!我的资源非常有限,所以有没有办法找出我需要多少资源,这样我就可以丢弃内存中可能太大的图像?一张 4.5 MB 的 JPEG 图像可能占用近 4 GB 的内存...

我有点希望

ImageIO.read(...)
有一个选项,这样我就可以告诉它“如果在某个时候你看到它需要超过 X 字节,就把所有东西都扔掉再回来找我。”我该如何解决这个问题?

编辑:我忘记了在这个库中有一些方法可以处理流。在我将图像下载到内存中之前,我会检查它是否允许我获得分辨率。

java bufferedimage javax.imageio
2个回答
0
投票

不完全作为答案,但每个红绿蓝都存储为一个 int(每个 int 4 个字节),如果每个像素都有 Alpha 通道,它也是 4 个字节的浮点数。 5000*5000 25,000,000 * 4 100,000,000 100mb 然后是用于保存和操作它的类足迹实例,可能是 50mb。所以可能还有 250mb 用于完成操作。 建议 Java 成像经常调用 System.gc() 以在每次对图像进行“操作”后通知清除垃圾,例如加载(打开)图像后立即执行,然后对其进行每个常规操作。 在启动命令行上设置垃圾收集器(例如 G1 gc)或服务器中启动命令的 env 变量属性也非常重要,例如仅示例:/bin/setenv.sh 并在其命令行或服务器中的类似设置堆栈内存 1mb 堆和最小最大内存


0
投票

好吧,我想通了,但它可能不适用于所有设置:我们可以做的是获取所需的信息而不加载整个图像,这是我不知道忘记它是可能的。我们得从

ImageIO.createImageInputStream(...)
开始。正如它的文档所说,它接受任何
Object
,如果它不能从中生成流,则可能返回
null
ImageIO
在设计时考虑了第 3 方插件,因此您应该知道进行空检查是否明智。

// in my case imgURL is a java.net.URL instance
ImageInputStream imgStream = ImageIO.createImageInputStream(imgURL.openStream());

然后我们可以得到该类型图像的

ImageReader
ImageIO.getImageReaders(imgStream).next();
),将其用于我们自己的图像,并要求它告诉我们图像的宽度和高度:

try (imgStream) {
    ImageReader reader = ImageIO.getImageReaders(imgStream).next();
    reader.setInput(imgStream);

    int width = reader.getWidth(0);
    int height = reader.getWidth(0);

    if (width * height > 3_000_000) {
        System.err.println("Too big!");
        return;
    }
}

这不是一个完美的解决方案:根据您的

ImageIO
插件,有几件事情可能会失败,但它是一个完美的 viable 对于许多情况,我认为,包括我的情况。我只是假设我使用的每个插件实现都会检查元数据中报告的尺寸是否与文件其余部分中描述的像素不匹配......

© www.soinside.com 2019 - 2024. All rights reserved.