Java 可执行文件无法读取资源文件夹 [重复]

Java executable can not read resources folder [duplicate]

提问人:heapyams 提问时间:11/12/2023 最后编辑:user207421heapyams 更新时间:11/12/2023 访问量:52

问:

我现在正在为这个问题苦苦挣扎一段时间,并想问一下你们中是否有人有使用可执行文件中的 Java 资源文件的经验。

我有一个使用 maven 直接将 jar 转换为 exe 的 java 应用程序构建。

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;
}

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

经过调查,我发现因为我是在 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 文件-IO EXE

评论

0赞 user207421 11/12/2023
资源文件夹不是目录,不能列出。资源也不是文件。在 IDE 中执行时,资源来自文件系统,而不是 JAR 文件。

答:

0赞 rzwitserloot 11/12/2023 #1

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

这是你的问题。您不能 LIST 资源,句点。

一个严重的问题是,如果你的资源没有被破坏,上面的代码可以“正常工作”。这个想法更健壮,甚至在某些平台上也有效(你试图获取一个目录作为资源并打开它,在某些平台上你得到一个列表),但它不受支持 - 规范没有提到类加载器必须支持这一点,事实上,许多人不支持。

结论很简单:不可能列出资源。如果你遇到一个说它可以的库,那它就是在撒谎:充其量它只是一个只在特定条件下起作用的黑客。

标准的解决方法是您应该为此使用的 - Service Loaders。

该概念的工作原理如下:

  • 程序员要么手动写出来,要么使用构建系统插件,最终得到一个包含您感兴趣的列表的文本文件。然后,此文本文件将包含在硬编码路径中。
  • 然后,应用程序加载该文本文件 - 这很好。资源系统无法列出目录的内容。但是,在已知位置为您提供文本文件的完整内容没有问题。
  • 然后,此列表用于加载您需要加载的任何内容。

甚至还有一个内置到 java 中的类可以做到这一点:.您应该在网络上搜索带有该术语的教程,您会发现很多。ServiceLoader

有很多库和博客文章试图解决这个问题。例如,有些人将资源作为 URL 获取,然后尝试解析这些资源,例如打开 jar 文件。问题是,ClassLoader 系统是完全抽象的。ClassLoader 可以从网络加载资源,动态解密它们,或者将它们整块布组成。而且它们不必支持作为基元列出。因此,任何此类尝试都是不完整的。使用服务加载器范式,因为它是“完整的”——无论类加载器实现如何,它都能工作。