我现在在这个问题上苦苦挣扎了一段时间,想问问你们中是否有人有在可执行文件中处理 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;
}
此函数会导致异常作为 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
我也知道必须有某种可行的解决方案,因为以下代码在与“文件”文件夹相同的文件夹层次结构级别中提供了测试文件的内容:
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 的执行导致了这种情况。
new File(Utillity.class.getResource("files").getPath()).list();
这就是你的问题。 您无法列出资源,期间。
一个严重的问题是,如果您的资源没有受到干扰,上面的代码可以“正常”工作。这个想法的更强大的实现甚至可以在某些平台上工作(您尝试获取目录作为资源并打开它,在某些平台上您会得到一个列表),但是不支持 - 规范没有提到类加载器必须支持这一点,事实上,很多不支持。
结论很简单:列出资源是不可能的。如果你遇到一个声称可以的库,那它就是在撒谎:充其量它只是一种仅在某些条件下有效的 hack。
标准解决方法是您应该使用的方法 - 服务加载器。这个概念的工作原理如下:
ServiceLoader
。您应该在网上搜索包含该术语的教程,您会发现很多。有很多库和博客文章试图解决这个问题。例如,有些将资源作为 URL 获取,然后尝试解析这些资源,例如打开 jar 文件。问题是,类加载器系统是完全抽象的。类加载器可以从网络加载资源,动态解密它们,或者将它们组合起来。
而且它们不必支持将列表作为原语。因此,任何此类尝试都是不完整的。使用服务加载器范例,因为它是“完整的” - 无论类加载器实现如何,它都可以工作。