Java 9 ImageIO read\write 给出的结果与 Java 8 不同

Java 9 ImageIO read\write gives a different result than Java 8

以下测试在 Java 9 上失败,但在 Java 8 上通过:

@Test
public void getImage_SetValueUsingConstructor_ShouldReturnCorrectValue() throws Exception {
        String base64ImageString = "iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAIAAAAmkwkpAAAAEUlEQVR42mNgQAP/wQAbBw4ANwsL9Zo6V30AAAAASUVORK5CYII=";
    byte[] rawImageBytes = Base64.getDecoder().decode(base64ImageString);

    ByteArrayInputStream bis = new ByteArrayInputStream(rawImageBytes);
    RenderedImage image = ImageIO.read(bis);

    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    ImageIO.write(image, "PNG", bos);
    byte[] imageBytesFromImage = bos.toByteArray();

    assertArrayEquals(imageBytesFromImage, rawImageBytes);
}

Java 9 输出: 数组首先在元素 [42] 处不同; 预期:94 实际:-38

任何人都可以帮助我了解 Java 9 中发生了什么变化,有没有办法编写此代码以使其适用于 Java 8 和 9?

正如@Holger 在评论中指出的那样,确实是测试存在缺陷。虽然相同的 Base64 表示会给出相同的图像,但不同的 Base64 表示并不意味着图像数据不同。这可能只意味着相同的图像数据被不同地编码,并且将解码为完全相同的图像(这里就是这种情况)。

你的测试过去没有错误地通过的原因可能是你使用了 Java 8 PNGImageWriter(或更早的版本,自 Java 1.4 以来它并没有太大变化),这是在您执行 ImageIO.write(image, "PNG", output) 时使用的 writer 插件,用于对图像进行编码并从中创建 Base64 表示。如果您从另一个 program/library 创建的文件创建了字节的 Base64 表示形式,它几乎肯定会有所不同。

您应该重写您的测试,但是我不太清楚您要在这里测试什么。

如果您只关心像素数据,您可以遍历像素并测试是否相等:

BufferedImage original = ImageIO.read(..);
BufferedImage current = ImageIO.read(..);

assertEquals(original.getWidth(), current.getWidth());
assertEquals(original.getHeight(), current.getHeight());

for (int y = 0; y < original.getHeight(); y++) {
    for (int x = 0; x < original.getWidth(); x++) {
        assertEquals(original.getRGB(x, y), current.getRGB(x, y));
    }
}

如果您还需要保留元数据,您还需要在那里测试是否相等。但是 PNG 并没有真正包含很多有趣的元数据,所以我怀疑你是否需要它。

感谢 Holger 的评论,我所做的是从字节数组中解码图像,然后比较两个图像的 dataBuffer。

下面的测试在 Java 8 和

上都通过了
@Test
public void imageTest() throws Exception {
    String base64ImageString = "iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAIAAAAmkwkpAAAAEUlEQVR42mNgQAP/wQAbBw4ANwsL9Zo6V30AAAAASUVORK5CYII=";
    byte[] rawImageBytes = Base64.getDecoder().decode(base64ImageString);

    ByteArrayInputStream bis = new ByteArrayInputStream(rawImageBytes);
    RenderedImage image = ImageIO.read(bis);

    ByteArrayOutputStream bos = new ByteArrayOutputStream();

    ImageIO.write(image, "PNG", bos);
    byte[] imageBytesFromImage = bos.toByteArray();

    //assertArrayEquals(imageBytesFromImage, rawImageBytes); //fails on Java 9!

    bis = new ByteArrayInputStream(imageBytesFromImage);
    RenderedImage image2 = ImageIO.read(bis);

    DataBuffer dbA = image.getData().getDataBuffer();
    int sizeA = dbA.getSize();
    DataBuffer dbB = image2.getData().getDataBuffer();
    int sizeB = dbB.getSize();
    // compare data-buffer objects //

    assertEquals(sizeA, sizeB);
    for (int i = 0; i < sizeA; i++) {
        assertEquals(dbA.getElem(i), dbB.getElem(i));

    }
}

比较图像代码取自:How to compare images for similarity using java