将文件加载为InputStream的不同方法


216

之间有什么区别:

InputStream is = this.getClass().getClassLoader().getResourceAsStream(fileName)

InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName)

InputStream is = this.getClass().getResourceAsStream(fileName)

什么时候每个人比另一个人更合适?

我要读取的文件作为读取文件的类在类路径中。我的类和文件位于同一jar中,并打包在EAR文件中,并部署在WebSphere 6.1中。

Answers:


289

关于fileName您通过的方式的解释存在细微的差异。基本上,您有2种不同的方法:ClassLoader.getResourceAsStream()Class.getResourceAsStream()。这两种方法将以不同的方式定位资源。

在中Class.getResourceAsStream(path),该路径被解释为从中调用该类的包的本地路径。例如调用,String.getResourceAsStream("myfile.txt")将在您的类路径中的以下位置查找文件:"java/lang/myfile.txt"。如果您的路径以开头/,则它将被视为绝对路径,并且将从类路径的根开始搜索。因此,调用String.getResourceAsStream("/myfile.txt")将在您的类路径中查看以下位置./myfile.txt

ClassLoader.getResourceAsStream(path)将所有路径视为绝对路径。因此,调用String.getClassLoader().getResourceAsStream("myfile.txt")String.getClassLoader().getResourceAsStream("/myfile.txt")都会在您的类路径中的以下位置查找文件:./myfile.txt

每当我在这篇文章中提到一个位置时,它可能是您文件系统本身中的一个位置,也可能是相应的jar文件中的一个位置,具体取决于您从中加载资源的Class和/或ClassLoader。

在您的情况下,您是从Application Server加载类,因此应使用Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName)而不是this.getClass().getClassLoader().getResourceAsStream(fileName)this.getClass().getResourceAsStream()也可以。

阅读本文以获得有关该特定问题的更多详细信息。


对Tomcat 7及更低版本用户的警告

该问题的答案之一表明,对于Tomcat 7,我的解释似乎不正确。我试图四处看看,为什么会这样。

因此,我研究WebAppClassLoader了几种版本的Tomcat 的源代码。findResource(String name)在Tomcat 6和Tomcat 7中,其实现(最终负责产生所请求资源的URL)实际上是相同的,但在Tomcat 8中则有所不同。

在版本6和7中,该实现不尝试标准化资源名称。这意味着在这些版本中,classLoader.getResourceAsStream("/resource.txt")虽然可能会产生与classLoader.getResourceAsStream("resource.txt")事件不同的结果(因为Javadoc指定了结果)。[源代码]

但是,在版本8中,对资源名称进行了规范化,以确保资源名称的绝对版本是所使用的版本。因此,在Tomcat 8中,上述两个调用应始终返回相同的结果。[源代码]

因此,在使用Tomcat 8 ClassLoader.getResourceAsStream()Class.getResourceAsStream()更低版本的Tomcat版本时,必须格外小心。还必须牢记class.getResourceAsStream("/resource.txt")实际调用的内容classLoader.getResourceAsStream("resource.txt")/除去了开头)。


2
我很确定这与的getClass().getResourceAsStream("/myfile.txt")行为有所不同getClassLoader().getResourceAsStream("/myfile.txt")
布莱恩·戈登

@BrianGordon:他们的行为没有差异。实际上,Class.getResourceAsStream(String)的javadoc 说了以下内容:“此方法委托给该对象的类加载器。”,然后给出一堆规则,说明如何将相对路径转换为绝对路径,然后再委派给类加载器。
LordOfThePigs

@LordOfThePigs查看实际源。如果提供绝对路径,则Class.getResourceAsStream会去除前导的正斜杠。
Brian Gordon

4
@BrianGordon:这使得它的行为与ClassLoader.getResourceAsStream()完全相同,因为后者会将所有路径都解释为绝对路径,无论它们是否以斜杠开头。因此,只要您的路径是绝对路径,这两种方法的行为都相同。如果您的路径是相对的,则行为会有所不同。
LordOfThePigs

我找不到getClassLoader()String,它是一个错误,或者需要延期?
AaA

21

使用MyClass.class.getClassLoader().getResourceAsStream(path)与您的代码相关联的负载的资源。使用MyClass.class.getResourceAsStream(path)的快捷方式,并为您的类的包中封装的资源。

使用Thread.currentThread().getContextClassLoader().getResourceAsStream(path)来获取在客户端代码的一部分资源,而不是紧紧界给调用代码。您应该对此谨慎,因为线程上下文类加载器可能指向任何东西。


6

普通旧Java 7上的普通旧Java和其他依赖项都没有显示出区别...

我放入file.txtc:\temp\然后放入c:\temp\类路径。

只有一种情况,即两个呼叫之间存在差异。

class J {

 public static void main(String[] a) {
    // as "absolute"

    // ok   
    System.err.println(J.class.getResourceAsStream("/file.txt") != null); 

    // pop            
    System.err.println(J.class.getClassLoader().getResourceAsStream("/file.txt") != null); 

    // as relative

    // ok
    System.err.println(J.class.getResourceAsStream("./file.txt") != null); 

    // ok
    System.err.println(J.class.getClassLoader().getResourceAsStream("./file.txt") != null); 

    // no path

    // ok
    System.err.println(J.class.getResourceAsStream("file.txt") != null); 

   // ok
   System.err.println(J.class.getClassLoader().getResourceAsStream("file.txt") != null); 
  }
}

非常感谢,对我来说,我只工作了'J.class.getResourceAsStream(“ file.txt”)'
abbasalim

3

此处的所有这些答案以及该问题的答案均表明,加载绝对URL(例如“ /foo/bar.properties”)与class.getResourceAsStream(String)和对待相同class.getClassLoader().getResourceAsStream(String)。并非如此,至少在我的Tomcat配置/版本(当前为7.0.40)中没有。

MyClass.class.getResourceAsStream("/foo/bar.properties"); // works!  
MyClass.class.getClassLoader().getResourceAsStream("/foo/bar.properties"); // does NOT work!

抱歉,我绝对没有令人满意的解释,但是我想tomcat会在类加载器上做一些肮脏的把戏和他的黑魔法,并造成差异。我class.getResourceAsStream(String)过去经常使用,没有任何问题。

PS:我也把这个贴在这里


也许tomcat决定不遵守规范,并且不将所有传递的路径都ClassLoader.getResourceAsStream()视为绝对路径吗?这是合理的,因为如上面的某些评论中所述,Class.getResourceAsStream实际上调用了getClassLoader()。getResourceAsStream`,但去除了任何前导斜杠。
LordOfThePigs

签入Java SE的源代码后,我认为我会得到答案:两者Class.getResourceAsStream()ClassLoader.getResourceAsStream()最终导致调用ClassLoader.findResource()该方法,这是一个受保护的方法,其默认实现为空,但其javadoc明确声明“类加载器实现应覆盖此方法以指定在何处寻找资源”。我怀疑tomcat对此特定方法的实现可能存在缺陷。
LordOfThePigs 2014年

我也比较的实施WebAppClassLoader.findResource(String name)的Tomcat 7与的Tomcat的8,它似乎有一个关键的区别。Tomcat 8通过添加前导(/如果不包含任何前导名)显式标准化资源名称,这将使所有名称成为绝对名称。Tomcat 7没有。这显然在Tomcat中7中的错误
LordOfThePigs

我在回答中添加了关于这一段的内容。
LordOfThePigs 2014年

0

在尝试了一些无法成功加载文件的方法之后,我记得我可以使用FileInputStream,它可以很好地工作。

InputStream is = new FileInputStream("file.txt");

这是将文件读取到中的另一种方法InputStream,它从当前运行的文件夹中读取文件。


它不是文件,而是资源。答案不正确。
罗恩侯爵

1
@EJP我最终得到这个SO答案,寻找不知道文件和资源之间的区别的加载文件的方法。我不会删除答案,因为它可能会对他人有所帮助。
安东尼奥·阿尔梅达

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.