当前位置: 首页 > 面试题库 >

如何从ImageIO中排除特定的TIFF阅读器?

宇文曦
2023-03-14
问题内容

堆栈:

  • Java-1.8.0_91
  • Scala-2.11.8
  • 库-it.geosolutions.imageio-ext imageio-ext-tiff 1.1.15

我们正在读取许多旧的TIF图像,并且由于某种原因读取的数据高度不一致-出于某些原因,在不同的运行中读取同一图像可能会成功或失败,但是-

javax.imageio.IIOException: Invalid component ID 3 in SOS
at com.sun.imageio.plugins.jpeg.JPEGImageReader.readImage(Native Method)
at com.sun.imageio.plugins.jpeg.JPEGImageReader.readInternal(JPEGImageReader.java:1236)
at com.sun.imageio.plugins.jpeg.JPEGImageReader.read(JPEGImageReader.java:1039)
at com.sun.media.imageioimpl.plugins.tiff.TIFFOldJPEGDecompressor.decodeRaw(TIFFOldJPEGDecompressor.java:654)
at com.sun.media.imageio.plugins.tiff.TIFFDecompressor.decode(TIFFDecompressor.java:2527)
at com.sun.media.imageioimpl.plugins.tiff.TIFFImageReader.decodeTile(TIFFImageReader.java:1137)
at com.sun.media.imageioimpl.plugins.tiff.TIFFImageReader.read(TIFFImageReader.java:1417)

代码是这样的:

import java.io.{ByteArrayInputStream, ByteArrayOutputStream}
import javax.imageio.ImageIO

def convertToPng(data: Array[Byte]): Array[Byte] = {
    val inputStream = new ByteArrayInputStream(data)
    val image = ImageIO.read(inputStream)
    val outputStream = new ByteArrayOutputStream(inputStream.available())
    ImageIO.write(image, "png", outputStream)
    outputStream.toByteArray
}

问题是ImageIO同时初始化2个TIFF读取器

 com.sun.media.imageioimpl.plugins.tiff.TIFFImageReader & 
 it.geosolutions.imageioimpl.plugins.tiff.TIFFImageReader

要么

 it.geosolutions.imageioimpl.plugins.tiff.TIFFImageReader
 com.sun.media.imageioimpl.plugins.tiff.TIFFImageReader

第一个失败,第二个成功。如何从ImageIO配置中排除com.sun.media.imageioimpl.plugins.tiff.TIFFImageReader?


问题答案:

这里的问题是ImageIO使用服务提供商接口(SPI)查找在运行时注册插件,并且在您的设置中找到了多个可以读取TIFF的插件。默认情况下,这些插件没有特定的顺序,这就是为什么有时您首先获得com.sun(JAI)TIFF插件,而有时首先获得it.geosolutions(Geosolutions)TIFF插件的原因。ImageIO.read(...)只会尝试第一个插件,如果失败则放弃。

如果可以的话,最简单的解决方案是从类路径中删除其中一个插件。但是我想你已经想到了这一点。还有许多其他方法可以解决此问题(我用Java给出了代码示例,因为这是我最熟悉的示例,因此我相信您可以在Scala中将其编写得更优雅;-)。

需要对代码进行最少更改的一种方法是在运行时注销“引导”代码中某处的JAI提供程序(确切的位置取决于应用程序,可能是静态初始化程序块或Web上下文侦听器,或者类似)。该IIORegistry有一个deregisterServiceProvider用于此目的的方法,去除从注册表中供应商,使其不能用于ImageIO

另一种选择是为提供者定义明确的顺序。如果出于某种原因(第三方要求/插件间的依存关系等)需要以单一格式拥有多个提供程序,这将很有用。该IIORegistry有一个setOrdering用于此目的,即允许设置方法
成对 两个服务提供商的排序,使得ImageIO总是喜欢一个先于另一个。

下面的代码显示了以上两个选项:

// Get the global registry
IIORegistry registry = IIORegistry.getDefaultInstance();

// Lookup the known TIFF providers
ImageReaderSpi jaiProvider = lookupProviderByName(registry, "com.sun.media.imageioimpl.plugins.tiff.TIFFImageReaderSpi");
ImageReaderSpi geoProvider = lookupProviderByName(registry, "it.geosolutions.imageioimpl.plugins.tiff.TIFFImageReaderSpi");

if (jaiProvider != null && geoProvider != null) {
    // If both are found, EITHER
    // order the it.geosolutions provider BEFORE the com.sun (JAI) provider
    registry.setOrdering(ImageReaderSpi.class, geoProvider, jaiProvider);

    // OR
    // un-register the JAI provider
    registry.deregisterServiceProvider(jaiProvider);
}
// New and improved (shorter) version. :-)
private static <T> T lookupProviderByName(final ServiceRegistry registry, final String providerClassName) {
    try {
        return (T) registry.getServiceProviderByClass(Class.forName(providerClassName));
    }
    catch (ClassNotFoundException ignore) {
        return null;
    }
}

上面的代码将确保Geosolutions
TIFF插件将始终由所使用ImageIO.read(...),并且您现有的代码应该可以正常工作(但现在很稳定)。

一个完全不同的选择是尝试使用所有已注册的TIFF插件读取数据,并使用第一个成功的插件。这比以前的代码更明确,但是需要重写图像读取代码:

byte[] data;
BufferedImage image;

try (ImageInputStream inputStream = ImageIO.createImageInputStream(new ByteArrayInputStream(data))) {
    Iterator<ImageReader> readers = ImageIO.getImageReaders(inputStream);

    // Try reading the data, using each reader until we succeed (or have no more readers)
    while (readers.hasNext()) {
        ImageReader reader = readers.next();

        try {
            reader.setInput(inputStream);
            image = reader.read(0);
            break; // Image is now correctly decoded
        }
        catch (Exception e) {
            // TODO: Log exception?
            e.printStackTrace();

            // Reading failed, try the next Reader
            inputStream.seek(0);
        }
        finally {
            reader.dispose();
        }
    }
}

当然,您可以组合使用以上选项,以兼顾两个方面的优势(即,如果一个阅读器出现故障,则订单稳定且回退)。



 类似资料:
  • 堆栈: Java-1.80_91 Scala-2.11.8 库-it.geosolutions.imageio-extImageio-ext-tiff 1.1.15 我们正在阅读大量旧的TIF图像,由于某些原因,读取高度不一致-由于某些原因,在不同的运行中读取同一图像可能会成功,也可能会失败- 代码如下: 问题是ImageIO同时初始化2个TIFF阅读器 或 第一个失败,第二个工作。如何从Imag

  • 你好,我正在尝试使用火花从文本文件中读取特定的行。 它可以用。获取第一行数据的第一个()命令。文本文件。如何访问文档的第n行?我需要java解决方案。

  • 如何才能缓解这种情况?

  • 我已经创建了可执行的jar文件(使用Eclipse),有一组图像(.png)文件要嵌入到jar中。因此,我添加了一个源文件夹,其中包含项目中文件夹中的所有图像。代码必须访问这些文件才能使用

  • 问题内容: 我们有一个与已删除的某些需求相关的软件包,但我们不想删除该代码,因为将来可能会再次需要它。因此,在我们现有的ant构建中,我们只是将该包排除在了jar中之外。这些类由于我们还删除了它们的依赖关系而无法编译,因此它们不能包含在构建中。 我正在尝试模仿Gradle中的功能,如下所示: 即使使用上面的exclude调用(并且我也尝试过不带方括号的尝试),gradle仍在尝试编译类,这会导致编

  • 问题内容: 我有以下从缓冲读取器读取数据的示例: 每当缓冲读取器中出现某些情况时(在这种情况下),将执行循环中的代码。在我的情况下,如果客户端应用程序将某些内容写入套接字,则将执行循环中的代码(服务器应用程序中)。 但是我不明白它是如何工作的。等待直到缓冲读取器中出现某些内容,当其中出现某些内容时,它将返回并执行循环中的代码。但是什么时候可以退货。 还有另一个问题。上面的代码摘自一个方法,我在线程