导出的 JAR 无法读取本地文件
Exported JAR unable to read local file
我的 Java 应用程序在名为 "assets" 的顶级文件夹中有一个名为 "classes" 的本地文件(一个简单的 List<String>
)。当我从 Eclipse 中 运行 下面的代码时,文件读取没有问题。
try { // read in classes from local file
ObjectInputStream inputStream =
new ObjectInputStream(new FileInputStream("assets\classes".replace("\", "/")));
listOfClasses = (List<String>) inputStream.readObject();
System.out.println("*** Reading local copy of classes file ***");
inputStream.close();
} catch (FileNotFoundException e) { e.printStackTrace();
} catch (ClassNotFoundException e) { e.printStackTrace();
} catch (IOException e) { e.printStackTrace(); }
当我将我的应用程序导出为 JAR 并 运行 它在另一台计算机上时,我在第 3 行得到 java.io.FileNotFoundException
。我将 "assets" 文件夹添加到构建路径,所以它应该在 JAR 中。
有人知道这里出了什么问题吗?
FileOutputStream
只是打开一个本地文件。它应该是绝对的(/home/user/...
或 c:\temp\...
)或相对于当前目录。例如,在 Windows 上,默认情况下这是包含 JAR 的目录。
如果您打算将 classes
文件 打包到 JAR 中,那么您应该使用 getResourceAsStream
,如@guleryuz 所述。该路径应该是从 CLASSPATH 根(即 JAR 文件的根)开始的绝对路径,如 "/assets/classes"
,或者(更好)相对于调用包的路径 class.
说到 classes,我强烈建议不要使用 getClass()
,因为它是一种多态方法。有两种获取 class 的方法:通过在对象上调用 getClass()
或访问静态 class
“字段”。如果您有一个名为 myObject
的 MyClass
实例,那么 myObject.getClass()
和 MyClass.class
本质上是同一件事。但!如果您的 class 被来自另一个包的 class 覆盖,例如 TheirClass
,那么对于 TheirClass
的实例,getClass()
将 return TheirClass.class
,即使您从 您的 代码调用 getClass()
。所以,除非你的 class 被声明为 final
,否则使用 getClass().getResource...
总是一个坏主意,因为你永远不知道什么时候有人会覆盖你的 class。而且你不应该知道甚至不关心!
考虑到所有这些,例如,您在名为 my.package
的包中有一个名为 MyClass
的 class。 JAR 可以具有以下结构:
my/
package/
MyClass.class
assets/
classes
然后,调用 MyClass.class.getResourceAsStream("assets/classes")
应该可以解决问题。或者,也可以是 MyClass.class.getResourceAsStream("/my/package/assets/classes")
。请注意,绝对路径 而不是 从项目目录开始,您可能在其中有 src
或 src/main
或您的构建系统使用的任何内容。它是相对于 CLASSPATH 根的(没错,absolute 路径相对于 CLASSPATH 根是 relative,即使这听起来令人困惑)。也就是说,它从你的包所在的目录开始。
最后但并非最不重要的一点是,如果您使用类似 Maven 的构建系统(例如 Maven 或 Gradle),那么 Java 源将转到 src/main/java
,但是像你的文件这样的资源文件会转到 src/main/resources
。它们最终位于您的 JAR 文件中的完全相同的位置。即src/main/java/package/MyClass.java
编译成package/MyClass.class
,src/main/resources/package/assets/classes
编译成package/assets/classes
。但是为了让构建系统弄清楚如何处理您的文件,您必须将它们放入正确的目录中:*.java
转到 java
,资源转到 resources
.
因此,假设采用类似 Maven 的约定,项目结构应该是
src/
main/
java/
my/
package/
MyClass.java
src/
main/
resources/
my/
package/
assets/
classes
给定这样的结构,您将得到一个具有上述结构的 JAR 文件。看起来有点疯狂,但一旦你掌握了它就会非常有用。
如果您没有使用类似 Maven 的构建系统,那么我不知道如何配置 Eclipse。不同的 IDE 可能有自己的约定,我一直将 Maven 与 Eclipse 结合使用。例如,一些 IDE 喜欢将 Java 源和资源放入同一目录树中。
我的 Java 应用程序在名为 "assets" 的顶级文件夹中有一个名为 "classes" 的本地文件(一个简单的 List<String>
)。当我从 Eclipse 中 运行 下面的代码时,文件读取没有问题。
try { // read in classes from local file
ObjectInputStream inputStream =
new ObjectInputStream(new FileInputStream("assets\classes".replace("\", "/")));
listOfClasses = (List<String>) inputStream.readObject();
System.out.println("*** Reading local copy of classes file ***");
inputStream.close();
} catch (FileNotFoundException e) { e.printStackTrace();
} catch (ClassNotFoundException e) { e.printStackTrace();
} catch (IOException e) { e.printStackTrace(); }
当我将我的应用程序导出为 JAR 并 运行 它在另一台计算机上时,我在第 3 行得到 java.io.FileNotFoundException
。我将 "assets" 文件夹添加到构建路径,所以它应该在 JAR 中。
有人知道这里出了什么问题吗?
FileOutputStream
只是打开一个本地文件。它应该是绝对的(/home/user/...
或 c:\temp\...
)或相对于当前目录。例如,在 Windows 上,默认情况下这是包含 JAR 的目录。
如果您打算将 classes
文件 打包到 JAR 中,那么您应该使用 getResourceAsStream
,如@guleryuz 所述。该路径应该是从 CLASSPATH 根(即 JAR 文件的根)开始的绝对路径,如 "/assets/classes"
,或者(更好)相对于调用包的路径 class.
说到 classes,我强烈建议不要使用 getClass()
,因为它是一种多态方法。有两种获取 class 的方法:通过在对象上调用 getClass()
或访问静态 class
“字段”。如果您有一个名为 myObject
的 MyClass
实例,那么 myObject.getClass()
和 MyClass.class
本质上是同一件事。但!如果您的 class 被来自另一个包的 class 覆盖,例如 TheirClass
,那么对于 TheirClass
的实例,getClass()
将 return TheirClass.class
,即使您从 您的 代码调用 getClass()
。所以,除非你的 class 被声明为 final
,否则使用 getClass().getResource...
总是一个坏主意,因为你永远不知道什么时候有人会覆盖你的 class。而且你不应该知道甚至不关心!
考虑到所有这些,例如,您在名为 my.package
的包中有一个名为 MyClass
的 class。 JAR 可以具有以下结构:
my/
package/
MyClass.class
assets/
classes
然后,调用 MyClass.class.getResourceAsStream("assets/classes")
应该可以解决问题。或者,也可以是 MyClass.class.getResourceAsStream("/my/package/assets/classes")
。请注意,绝对路径 而不是 从项目目录开始,您可能在其中有 src
或 src/main
或您的构建系统使用的任何内容。它是相对于 CLASSPATH 根的(没错,absolute 路径相对于 CLASSPATH 根是 relative,即使这听起来令人困惑)。也就是说,它从你的包所在的目录开始。
最后但并非最不重要的一点是,如果您使用类似 Maven 的构建系统(例如 Maven 或 Gradle),那么 Java 源将转到 src/main/java
,但是像你的文件这样的资源文件会转到 src/main/resources
。它们最终位于您的 JAR 文件中的完全相同的位置。即src/main/java/package/MyClass.java
编译成package/MyClass.class
,src/main/resources/package/assets/classes
编译成package/assets/classes
。但是为了让构建系统弄清楚如何处理您的文件,您必须将它们放入正确的目录中:*.java
转到 java
,资源转到 resources
.
因此,假设采用类似 Maven 的约定,项目结构应该是
src/
main/
java/
my/
package/
MyClass.java
src/
main/
resources/
my/
package/
assets/
classes
给定这样的结构,您将得到一个具有上述结构的 JAR 文件。看起来有点疯狂,但一旦你掌握了它就会非常有用。
如果您没有使用类似 Maven 的构建系统,那么我不知道如何配置 Eclipse。不同的 IDE 可能有自己的约定,我一直将 Maven 与 Eclipse 结合使用。例如,一些 IDE 喜欢将 Java 源和资源放入同一目录树中。