Java 可执行文件无法读取资源文件夹

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

我现在在这个问题上苦苦挣扎了一段时间,想问一下你们中是否有人在可执行文件中的java资源文件过期了。 我有一个使用 maven 构建的 java 应用程序,直接将 jar 转换为 exe。

pom.xml

<build>
        <resources>
            <resource>
                <directory>${basedir}/src/main/resources</directory>
                <includes>
                    <include>**/*</include>
                </includes>
            </resource>
        </resources>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.11.0</version>
                <configuration>
                    <source>20</source>
                    <target>20</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.openjfx</groupId>
                <artifactId>javafx-maven-plugin</artifactId>
                <version>0.0.8</version>
                <executions>
                    <execution>
                        <!-- Default configuration for running with: mvn clean javafx:run -->
                        <id>default-cli</id>
                        <configuration>
                            <mainClass>com.example.package/com.example.package.Main</mainClass>
                            <launcher>${project.artifactId}</launcher>
                            <jlinkZipName>${project.artifactId}</jlinkZipName>
                            <jlinkImageName>${project.artifactId}</jlinkImageName>
                            <noManPages>true</noManPages>
                            <stripDebug>true</stripDebug>
                            <noHeaderFiles>true</noHeaderFiles>
                            <compress>2</compress>
                        </configuration>
                        <phase>package</phase>
                        <goals>
                            <goal>jlink</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>3.0.0</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>exec</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <executable>${project.basedir}\tools\warp-packer.exe</executable>
                    <arguments>
                        <argument>--arch</argument>
                        <argument>windows-x64</argument>

                        <argument>--input_dir</argument>
                        <argument>${project.build.directory}\${project.artifactId}</argument>

                        <argument>--exec</argument>
                        <argument>bin\${project.artifactId}.bat</argument>

                        <argument>--output</argument>
                        <argument>${project.build.directory}\${project.artifactId}.exe</argument>
                    </arguments>
                </configuration>
            </plugin>
        </plugins>
    </build>

代码包含一个尝试读取资源文件夹中所有文件的函数:

public static String [] getFiles(){
    String[] outStrings = new File(Utillity.class.getResource("files").getPath()).list();
    System.out.println(Arrays.toString(out_strings));
    return outStrings;
}

此函数会导致将 exectuet 作为 exe 执行时出现异常,因为 getResource("files") 返回 null。在 IDE 中一切正常。

经过调查,我发现因为我在 jar 环境中工作,所以我必须小心文件。 在看了几个教程之后,我尝试找出一条全局路径。这失败了。例如这段代码:

String jarPath = CallingPythonScripts.class
    .getProtectionDomain()
    .getCodeSource()
    .getLocation()
    .toURI()
    .getPath();
System.out.println("JAR Path : " + jarPath);

// Get name of the JAR file
String jarName = jarPath.substring(jarPath.lastIndexOf("/") + 1);
System.out.println("JAR Name: " + jarName);

提供以下输出:

JAR Path: /com.example.package
JAR Name: com.example.package

我也知道必须有某种可行的解决方案,因为以下代码在与“files”文件夹相同的文件夹层级中提供了测试文件的内容:

InputStream is = CallingPythonScripts.class.getResourceAsStream("demo.txt");

try (
   InputStreamReader isr = new InputStreamReader(is, StandardCharsets.UTF_8);
   BufferedReader br = new BufferedReader(isr)) {

       br.lines().forEach(line -> System.out.println(line));
}catch (IOException e){
    e.printStackTrace();
}

“files/demo2.txt”也不是“demo.txt”,而是可读的。对于文件夹来说它本身就是= null。

我很确定它与内部文件表示有关。此外,在 IDE 中一切都工作正常,只是 exe 的执行导致了这种情况。 感谢您提前输入。

java maven file-io exe
1个回答
0
投票

new File(Utillity.class.getResource("files").getPath()).list();

这就是你的问题。 您无法列出资源,期间。

一个严重的问题是,如果您的资源没有受到干扰,上面的代码可以“正常”工作。这个想法的更强大的实现甚至可以在某些平台上工作(您尝试获取目录作为资源并打开它,在某些平台上您会得到一个列表),但是不支持 - 规范没有提到类加载器必须支持这一点,事实上,很多不支持。

结论很简单:列出资源是不可能的。如果你遇到一个声称可以的库,那它就是在撒谎:充其量它只是一种仅在某些条件下有效的 hack。

标准解决方法是您应该使用的方法 - 服务加载器。

这个概念的工作原理如下:

    程序员可以手动写出它,或者使用构建系统插件,最终得到一个包含您感兴趣的列表的文本文件。然后该文本文件包含在硬编码路径中。
  • 应用程序然后加载
  • 该文本文件 - 这很好。资源系统无法列出目录的内容。但它可以毫无问题地为您提供已知位置的文本文件的完整内容。
  • 此列表用于加载您需要加载的任何内容。
甚至有一个嵌入到 java 中的类可以做到这一点:

ServiceLoader

。您应该在网上搜索包含该术语的教程,您会发现很多。

有很多库和博客文章试图解决这个问题。例如,有些将资源作为 URL 获取,然后尝试解析这些资源,例如打开 jar 文件。问题是,类加载器系统是完全抽象的。类加载器可以从网络加载资源,动态解密它们,或者将它们组合起来。

而且它们不必支持将列表作为原语。因此,任何此类尝试都是不完整的。使用服务加载器范例,因为它是“完整的” - 无论类加载器实现如何,它都可以工作。

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