英文:
How to correctly setup resources in a exportable library
问题
在我创建的一个库中,我有以下行来检索我的资源:
private static final File fragment = new File(DefaultShader.class.getClassLoader().getResource("file.txt").getFile());
但是当我将这个库导出为一个JAR文件,并尝试在另一个应用程序中使用它时,当在内部读取相同的行时,我会收到一个I/O错误,因为它试图访问JAR文件。
java.io.FileNotFoundException: file:\C:\Users\me\test\libs\lib.jar!\file.txt
Node.js通过提供返回脚本的运行位置路径的方法使得这更加容易,显然在Java中没有类似的方法,或者我只是找不到。有什么方法可以解决这个问题?
英文:
In a library I made, I have the following line to retrieve my resources:
private static final File fragment = new File(DefaultShader.class.getClassLoader().getResource("file.txt").getFile());
But when I export this library to a JAR and attempt to use it in another application, when this same line is read internally I get an I/O error because it's trying to access the JAR.
java.io.FileNotFoundException: file:\C:\Users\me\test\libs\lib.jar!\file.txt
Nodejs would make this easier by providing methods that returns the runtime location path of a script, apparently there is not an equivalent in Java or just couldn't find it. How can I get around this issue?
答案1
得分: 1
不要调用URL的getFile方法。它不会返回有效的文件名,只会返回URL的路径部分。
嵌入在.jar文件中的资源不是File,您不能将其视为File进行引用。
幸运的是,您不需要File对象。您可以直接使用getResourceAsStream来读取:
DefaultShader.class.getResourceAsStream("/file.txt")
显然,您不应该将InputStream存储在static final
字段中。但是,您可以轻松地创建一个方法代替:
private static InputStream readFileData() {
return DefaultShader.class.getResourceAsStream("/file.txt");
}
请注意,资源应该放在包中,就像类一样。将资源放在.jar的根目录中就像将文件写入驱动器的根目录或用户的主目录:如果任何其他程序选择使用相同的文件名,两个程序的结果都将有问题,至少可以这么说。
类似地,如果您的资源位于.jar的根目录中,并且另一个库恰好也在其自己的.jar的根目录中存储了具有相同名称的资源,将会产生冲突,也许您的资源会根据当前的类路径定义而被getResource*方法加载,也可能不会加载您的资源。(这个问题不适用于Java 9+的模块化程序,但仍然最好将资源放在包中。)
将资源放在使用它的类相同的包中被Java SE认为是一种良好的做法:getResource
和getResourceAsStream
方法默认情况下会期望在同一个包中找到它。如果字符串参数不以斜杠开头,这些方法会认为它在同一个包中。
这会在DefaultShader类的相同包中查找file.txt:
DefaultShader.class.getResourceAsStream("file.txt")
而这将在类路径中每个.jar的根目录中查找file.txt:
DefaultShader.class.getResourceAsStream("/file.txt")
英文:
Never call the getFile method of URL. It does not return a valid file name. It only returns the path portion of a URL.
A resource embedded in a .jar file is not a File and you cannot refer to it as a File.
Fortunately, you don’t need a File object. You can read it directly using getResourceAsStream:
DefaultShader.class.getResourceAsStream("/file.txt")
Obviously, you should not store an InputStream in a static final
field. But you can easily make a method instead:
private static InputStream readFileData() {
return DefaultShader.class.getResourceAsStream("/file.txt");
}
Be aware that resources should be placed in packages, just like classes. Placing a resource in the root of a .jar is like writing a file to a drive’s root directory or the user’s home directory: if any other program chooses to use the same filename, the results for both programs will be problematic, to say the least.
Similarly, if your resource is in the root of your .jar, and another library also happens to store a resource with the same name in the root of its own .jar, there will be a conflict, and it may or may not be your resource which gets loaded by the getResource* methods, depending on the current classpath definition. (This concern doesn’t apply to Java 9+ modular programs, but it’s still a good idea to keep resources in packages.)
The practice of putting a resource in the same package as the class that uses it is considered a good practice by Java SE: the getResource
and getResourceAsStream
methods are designed to expect it in the same package as the class by default. If the string argument does not start with a slash, those methods assume it’s in the same package.
This looks for file.txt in the same package as the DefaultShader class:
DefaultShader.class.getResourceAsStream("file.txt")
Whereas this will look for file.txt in the root of every .jar in the classpath:
DefaultShader.class.getResourceAsStream("/file.txt")
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论