You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot单元测试中ImageIO找不到阅读器问题求助

解决Spring Boot单元测试中ImageIO "No reader found" 问题

首先,我注意到你的测试失败有两个核心原因,咱们一步步来拆解解决:

1. 首要问题:InputStream被重复读取导致数据为空

在resizeJpgTest方法里,你把同一个InputStream先传给getExifData(is)读取原始EXIF,接着又传给adapter.resize(is, 800)做缩放——这时候流的指针已经走到末尾了,resize拿到的是空流,生成的结果自然是无效的JPEG数据,ImageIO根本没法识别,所以抛出"No reader found"。

修复方法:复制流数据,避免重复使用

先把图片资源读入字节数组,再分别生成新的InputStream供EXIF读取和缩放使用,确保每次操作都有完整的数据流:

@Test
public void resizeJpgTest() throws IOException, ResizeException {
    Resource resource = new ClassPathResource("img/test2.jpg");
    Assert.assertNotNull("image to check resize exists", resource);
    Assert.assertTrue("image to check resize exists", resource.exists());
    
    // 先把图片读入字节数组,避免流重复使用导致数据为空
    byte[] imageBytes = Files.readAllBytes(resource.getFile().toPath());
    
    // 读取原始EXIF数据
    try (ByteArrayInputStream exifIs = new ByteArrayInputStream(imageBytes)) {
        CompoundDirectory sourceExif = getExifData(exifIs);
        Assert.assertNotNull("Source exif have been read", sourceExif);
    }
    
    // 执行缩放操作
    try (ByteArrayInputStream resizeIs = new ByteArrayInputStream(imageBytes)) {
        var result = adapter.resize(resizeIs, 800);
        
        // 验证缩放结果
        try (var resultIs = new ByteArrayInputStream(result)) {
            ImageInputStream imageIs = ImageIO.createImageInputStream(resultIs);
            Iterator<ImageReader> readers = ImageIO.getImageReaders(imageIs);
            if (!readers.hasNext()) {
                throw new IllegalArgumentException("No reader found");
            }
            ImageReader reader = readers.next();
            reader.setInput(imageIs);
            Assert.assertEquals("JPEG", reader.getFormatName());
            
            // 重置结果流指针,重新读取EXIF数据
            resultIs.reset();
            CompoundDirectory exif = getExifData(resultIs);
            Assert.assertNotNull(exif);
            
            Assert.assertTrue("width should be <= 800 now", reader.getWidth(0) <= 800);
            Assert.assertTrue("height should be <= 800 now", reader.getHeight(0) <= 800);
            Assert.assertEquals("Orientation has not changed", 5, exif.getEntryByFieldName("orientation").getValue());
        }
    } catch (Exception e) {
        throw new RuntimeException("error while reading result", e);
    }
}

2. 确保TwelveMonkeys ImageIO插件在测试环境正确注册

虽然你在init方法里调用了插件扫描,但Spring Boot测试的类加载器可能有特殊处理,导致SPI没有被正确发现。可以显式注册TwelveMonkeys的SPI实现,确保测试环境能加载到阅读器:

@Before
public void init() {
    IIORegistry registry = IIORegistry.getDefaultInstance();
    // 显式注册TwelveMonkeys的JPEG/TIFF读写SPI
    registry.registerServiceProvider(new JPEGImageReaderSpi());
    registry.registerServiceProvider(new JPEGImageWriterSpi());
    registry.registerServiceProvider(new TIFFImageReaderSpi());
    registry.registerServiceProvider(new TIFFImageWriterSpi());
    // 再执行一次插件扫描,确保没有遗漏
    ImageIO.scanForPlugins();
}

3. 优化EXIF读取方法的流处理

你的getExifData方法里,流的生命周期管理可以更严谨,避免潜在的流泄漏或指针偏移问题:

private CompoundDirectory getExifData(InputStream inputStream) throws IOException {
    try (ImageInputStream imageIs = ImageIO.createImageInputStream(inputStream)) {
        List<JPEGSegment> exifSegments = JPEGSegmentUtil.readSegments(imageIs, JPEG.APP1, "Exif");
        if (!exifSegments.isEmpty()) {
            InputStream exifData = exifSegments.get(0).data();
            exifData.read(); // Skip 0-pad for Exif in JFIF
            
            try (ImageInputStream exifStream = ImageIO.createImageInputStream(exifData)) {
                return (CompoundDirectory) new TIFFReader().read(exifStream);
            }
        }
    }
    return null;
}

额外建议

  • 确保项目依赖中TwelveMonkeys的库是compile/runtime范围,不是仅provided,测试环境需要能加载到这些类。
  • 如果使用Spring Boot 2.4+,可以检查测试的类加载器配置,避免插件被类加载器隔离。

按照这些修改后,你的单元测试应该能正常运行,既可以正确读取EXIF数据,又能验证缩放后的图片尺寸和EXIF完整性。

内容的提问来源于stack exchange,提问作者mcfly

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 20:17:32