14

所以我完成了我的应用程序的编码,以旋转需要 JAI 来操作 TIFF 的 TIFF 图像。

在 Eclipse 下工作时它工作正常,但是每当我为库构建一个胖 jar 并创建一个实现该库时(根据http://fjep.sourceforge.net/fjeptutorial.html),当我执行 java -jar Push 时。 jar \path\to\dir,它会一直运行,直到遇到它正在压缩和保存的部分:

TIFFImageWriterSpi tiffspi = new TIFFImageWriterSpi();
ImageWriter writer = tiffspi.createWriterInstance();
//Iterator<ImageWriter> iter =  ImageIO.getImageWritersByFormatName("TIFF");
//ImageWriter writer = iter.next();

ImageWriteParam param2 = writer.getDefaultWriteParam();
param2.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);

param2.setCompressionType("LZW");
param2.setCompressionQuality(0.0f);
File fOutputFile = workArea[i];
ImageOutputStream ios = ImageIO.createImageOutputStream(fOutputFile);
writer.setOutput(ios);

if (frontPage == 1)
{
     writer.write(null, new IIOImage(pg1, null, null), param2);
     writer.writeInsert(-1, new IIOImage(pg2, null, null), param2);
}
else if (frontPage == 2)
{
     writer.write(null, new IIOImage(pg2, null, null), param2);
     writer.writeInsert(-1, new IIOImage(pg1, null, null), param2);
}

remaining = remaining - 1;
    if (remaining > 0)
     System.out.println(remaining + " remaining.");
else
     System.out.println("Done.");

它在该部分的第一行爆炸并显示以下消息:

 Exception in thread "main" java.lang.IllegalArgumentException: vendorName == null!
 ....rest of stack trace.
4

7 回答 7

30

由于我花了相当多的时间调试这个问题,我想我会在这里分享我的解决方案,尽管这个问题已经很老了。Srikanth 的第二个链接特别有用。

错误原因

JAI 需要为其某些深层内部结构提供供应商名称,特别是 javax.imageio.spi.IIOServiceProvider,它被许多(全部?)图像阅读器用于其低级 IO。字符串是什么并不挑剔,但不能为空。

ImageReaderSpi 类不是硬编码供应商名称,而是从 sun.media.imageioimpl.common.PackageUtil.getVendor() 获取供应商名称。这反过来从罐子的 MANIFEST.MF 中读取它。通常你链接到标准的 jai-imageio 包,所以 Sun 的供应商信息会被读取。但是,由于您正在制作一个胖 jar 文件,因此您将 Sun 的 MANIFEST.MF 替换为您自己的缺少所需信息的文件。

解决方案

在 MANIFEST.MF 文件中包含以下行:

规范-标题:Java Advanced Imaging Image I/O Tools
规格版本:1.1
规格供应商:Sun Microsystems, Inc.
实施-标题:com.sun.media.imageio
实施版本:1.1
实施供应商:Sun Microsystems, Inc.

每个属性的值可以是任何值(我使用了我的特定应用程序/版本/公司),只要定义了所有六个。

马文

如果你使用 maven 的组装插件来创建你的 fat jar,maven 可以自动包含正确的版本号等。pom.xml使用以下<archive>部分更新您的:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-assembly-plugin</artifactId>
    <configuration>
        <descriptorRefs>
            <descriptorRef>jar-with-dependencies</descriptorRef>
        </descriptorRefs>
        <archive>
            <manifest>
                <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
                <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
            </manifest>
            <manifestEntries>
                <Specification-Vendor>MyCompany</Specification-Vendor>
                <Implementation-Vendor>MyCompany</Implementation-Vendor>
            </manifestEntries>
        </archive>
    </configuration>
    <executions>
        <execution>
            <id>create-my-bundle</id>
            <phase>package</phase>
            <goals>
                <goal>single</goal>
            </goals>
        </execution>
    </executions>
</plugin>
于 2013-08-28T18:33:39.527 回答
5

我不得不使用这个ImageIO jar。它就像一个魅力。在这里找到它。

于 2011-12-05T13:37:33.110 回答
4

Quantum7 接受的答案解释了问题的根源,并且在Maven部分中,它提供了使用 Maven Assembly 插件生成包含依赖项的 JAR 时的解决方案。如果您改为使用 Maven Shade 插件来生成具有依赖项的 JAR,则配置会略有不同。您可以将以下内容添加到您的 Shade 插件的配置部分pom.xml

<configuration>
    <transformers>
        <transformer
                implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
            <manifestEntries>
                <Main-Class>com.conveyal.r5.R5Main</Main-Class>
                <Specification-Title>Java Advanced Imaging Image I/O Tools</Specification-Title>
                <Specification-Version>1.1</Specification-Version>
                <Specification-Vendor>Sun Microsystems, Inc.</Specification-Vendor>
                <Implementation-Title>com.sun.media.imageio</Implementation-Title>
                <Implementation-Version>1.1</Implementation-Version>
                <Implementation-Vendor>Sun Microsystems, Inc.</Implementation-Vendor>
                <Extension-Name>com.sun.media.imageio</Extension-Name>
            </manifestEntries>
        </transformer>
    </transformers>
</configuration>
于 2018-01-04T14:12:06.573 回答
2

免责声明

我遇到的问题略有不同,在尝试运行已编译的 jar 文件时出现“ClassNotFound”错误。我在研究时偶然发现了这个 SO 问题,所以对于其他与我一样遵循相同路径的人,你去吧。

ClassNotFound 错误的可能解决方案

对于以后可能会发现此问题的人,如果似乎没有其他工作,您可以尝试使用 Maven 的Apache Shader插件。是有关它的更多信息。

我没有足够的经验来告诉你它是如何做到的,但是 Apache Shader 将你项目中所有使用的依赖项打包到最终的 jar 文件中,这样所有的依赖项在它构建时都包含在 META-INF 文件夹中. 这会增加 jar 文件的大小(取决于您在项目中使用了多少库),但似乎也修复了 jar 无法从使用的外部库中找到类的问题。

要使用 Shader 插件,请将以下内容添加到您的 POM。我将它包含在依赖项标记之后和属性标记之前。

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <version>2.4.3</version>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>shade</goal>
                    </goals>
                    <configuration>
                        <filters>
                            <filter>
                                <artifact>*:*</artifact>
                                <excludes>
                                    <exclude>META-INF/*.SF</exclude>
                                    <exclude>META-INF/*.DSA</exclude>
                                    <exclude>META-INF/*.RSA</exclude>
                                </excludes>
                            </filter>
                        </filters>
                        <transformers>
                            <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                <manifestEntries>
                                    <Main-Class>com.package.name.className</Main-Class>
                                    <Build-Number>1</Build-Number>
                                </manifestEntries>
                            </transformer>
                        </transformers>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

注意:确保更改包和类名以反映项目的包和类名。

其他有用的链接:类似的堆栈溢出问题

于 2016-06-14T18:19:38.770 回答
2

(如果我有足够的声誉,本来会是对 Quantum7 的回答的评论)

我遇到了同样的问题。Quantum7 的回答拯救了这一天!然而,在放入 manifestEntries Specification-Vendor 和 Implementation-Vendor 之后,我的 fat jar 的执行仍然失败,但出现以下异常。注意是

版本 == 空!

不是

供应商名称 == 空!

java.util.ServiceConfigurationError: javax.imageio.spi.ImageReaderSpi: Provider com.sun.media.imageioimpl.plugins.jpeg.CLibJPEGImageReaderSpi could not be instantiated
        at java.util.ServiceLoader.fail(Unknown Source)
        at java.util.ServiceLoader.access$100(Unknown Source)
        at java.util.ServiceLoader$LazyIterator.nextService(Unknown Source)
        at java.util.ServiceLoader$LazyIterator.next(Unknown Source)
        at java.util.ServiceLoader$1.next(Unknown Source)
        at javax.imageio.spi.IIORegistry.registerApplicationClasspathSpis(Unknown Source)
        at javax.imageio.spi.IIORegistry.<init>(Unknown Source)
        at javax.imageio.spi.IIORegistry.getDefaultInstance(Unknown Source)
        at javax.imageio.ImageIO.<clinit>(Unknown Source)
        at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.IllegalArgumentException: version == null!
        at javax.imageio.spi.IIOServiceProvider.<init>(Unknown Source)
        at javax.imageio.spi.ImageReaderWriterSpi.<init>(Unknown Source)
        at javax.imageio.spi.ImageReaderSpi.<init>(Unknown Source)
        at com.sun.media.imageioimpl.plugins.jpeg.CLibJPEGImageReaderSpi.<init>(CLibJPEGImageReaderSpi.java:80)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
        at java.lang.reflect.Constructor.newInstance(Unknown Source)
        at java.lang.Class.newInstance(Unknown Source)
        ... 14 more

放入下面的 manifestEntries 修复了它。

<manifestEntries>
    <Specification-Title>Java Advanced Imaging Image I/O Tools</Specification-Title>
    <Specification-Version>1.1</Specification-Version>
    <Specification-Vendor>Sun Microsystems, Inc.</Specification-Vendor>
    <Implementation-Title>com.sun.media.imageio</Implementation-Title>
    <Implementation-Version>1.1</Implementation-Version>
    <Implementation-Vendor>Sun Microsystems, Inc.</Implementation-Vendor>
</manifestEntries>

在运行 fat jar 时,我确保下面代码中的三个值都不为空。

import com.sun.media.imageioimpl.common.PackageUtil;

public class ManifestCheck {
    public static void main(String[] args) {
        System.out.println(PackageUtil.getVendor());
        System.out.println(PackageUtil.getVersion());
        System.out.println(PackageUtil.getSpecificationTitle());
    }
}
于 2016-07-15T07:02:07.817 回答
1

这些可以帮助您解决问题。

如何使用源代码运行 jai-imageio

供应商名称 == 空

于 2011-08-13T16:11:11.363 回答
1

在努力寻找许多解决方案并为我工作后,我也遇到了同样的错误。

需要在代码中手动提供 venderName:

import com.sun.media.imageioimpl.common.PackageUtil;

    
    try {
        MainClass.afVenderNames(PackageUtil.class, "afreen", "afreen", "afreen");
    } catch (NoSuchFieldException e1) {
        e1.printStackTrace();
    } catch (SecurityException e1) {
        e1.printStackTrace();
    } catch (IllegalArgumentException e1) {
        e1.printStackTrace();
    } catch (IllegalAccessException e1) {
        e1.printStackTrace();
    }
    System.out.println(PackageUtil.getVendor());
    System.out.println(PackageUtil.getVersion());
    System.out.println(PackageUtil.getSpecificationTitle());
    
    
    
    public static void afVenderNames(Class<?> PackageUtil, String vendor, String version, String specTitle)
        throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
    Field vendorField = PackageUtil.getDeclaredField("vendor");
    vendorField.setAccessible(true);
    vendorField.set(null, vendor);

    Field versionField = PackageUtil.getDeclaredField("version");
    versionField.setAccessible(true);
    versionField.set(null, version);

    Field specTitleField = PackageUtil.getDeclaredField("specTitle");
    specTitleField.setAccessible(true);
    specTitleField.set(null, specTitle);
}

如果 vendorName 值不为 NULL,则无关紧要。我的代码在 Eclipse 中运行良好,但在导出的可运行 jar 中出现异常。

最终在编写上述代码并导出 jar 时工作。

于 2021-05-21T13:48:50.680 回答