为什么对 jar 中的资源使用 java.lang.ClassLoader.getResource

Why to use java.lang.ClassLoader.getResource for resource within jar

使用 Java 8,Tomcat 8+。

我遇到过这样的问题 - 我正在尝试读取 lib 中的 jar 中的 txt 资源。我使用了以下代码:

import java.io.File;
import org.apache.commons.io.FileUtils;

// ....    
// code within some class method
File file = new File(superService.getClass().getClassLoader()
                    .getResource("com/domain/example/sorry/confidential/little_text.txt")
                    .getFile())
return FileUtils.readFileToString(file, "UTF-8")

虽然它在我的 Intellij IDEA 中运行良好 运行,但在 Tomcat 服务器上部署后我遇到了这样的异常:

java.io.FileNotFoundException: File ‘file:/usr/local/tomcat/webapps/prod-lims/WEB-INF/lib/abc-1.2-SNAPSHOT.jar!com/domain/example/sorry/confidential/little_text.txt’ does not exist

之后,我在 SO 和其他站点中找到了很多答案,我需要对 JAR 中的资源使用 ClassLoader.getResourceAsStream() 或 Class.getResourceAsStream() 方法。当然,我尝试以旧式方式重新设计我的代码 - 瞧! - 现在可以使用了:

import java.io.BufferedReader;
import java.io.InputStreamReader;

// ....    
// code within some class method
InputStream inputStream = superService.getClass().getClassLoader()
               .getResourceAsStream("com/domain/example/sorry/confidential/little_text.txt")


StringBuilder resultStringBuilder = new StringBuilder();
BufferedReader br = new BufferedReader(new InputStreamReader(inputStream))
    try {
        String line;
        while ((line = br.readLine()) != null) {
            resultStringBuilder.append(line).append("\n");
        }
        } catch(Exception e) {
        //just a formal catch - don't mind for now
    }

return resultStringBuilder.toString()

好的,我对可行的解决方案很满意。但是我在任何地方都找不到任何明确的解释:为什么 getResourceAsStream 有效,同时 'File' 方法不适用于 JAR 中的资源?我很好奇,因为 superService.getClass().getClassLoader().getResource("com/domain/example/sorry/confidential/little_text.txt") returns 不为空,但是构造的文件有 canRead() == falseisExist() == false.

File、FileInputStream、FileUtils 等旨在访问文件系统,从文件系统的角度来看,嵌入在 JAR 中的 "file" 实际上并不是文件。因此,您只能通过实际用于从存档内部读取的工具访问这些嵌入式文件(基本上,JAR 只是一个奇特的 ZIP)。

顺便说一下,Java 中的 File 对象非空并不意味着该文件实际存在。你可以做到这一点:

File file = new File("I:\do\not.exist");

file 将是非空的(但 isReadable()isExist() 应该是假的)。这与您为资源获得的行为相同。

它可能在 IDE 中有效,因为文件 确实 实际上作为非存档文件存在于您的工作区中。