关于java:将文件加载为InputStream的不同方法

Different ways of loading a file as an InputStream

有什么区别:

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

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

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

什么时候每个人都比其他人更适合使用?

我要读取的文件作为我的类在类路径中读取该文件。我的类和文件在同一个JAR中,打包在一个EAR文件中,并部署在WebSphere6.1中。


对于您传递的fileName的解释方式有细微的差异。基本上,您有两种不同的方法: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文件中的一个位置,这取决于从中加载资源的类和/或类加载器。

在您的例子中,您是从应用服务器加载类的,因此您应该使用Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName),而不是this.getClass().getClassLoader().getResourceAsStream(fileName)this.getClass().getResourceAsStream()也将起作用。

阅读本文了解有关该特定问题的更多详细信息。

Tomcat 7及以下用户的警告

这个问题的答案之一是,我的解释对Tomcat7来说似乎是不正确的。我试着四处看看为什么会这样。

因此,我研究了Tomcat的WebAppClassLoader的源代码,了解了几个版本的Tomcat。在Tomcat 6和Tomcat 7中,findResource(String name)的实现(专门负责生成请求资源的URL)实际上是相同的,但在Tomcat 8中是不同的。

在版本6和7中,实现不会尝试规范化资源名称。这意味着,在这些版本中,classLoader.getResourceAsStream("/resource.txt")可能不会产生与classLoader.getResourceAsStream("resource.txt")事件相同的结果,尽管它应该(因为javadoc指定了这个结果)。[源代码]

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

因此,在8之前的Tomcat版本上使用ClassLoader.getResourceAsStream()Class.getResourceAsStream()时必须格外小心。您还必须记住,class.getResourceAsStream("/resource.txt")实际上称为classLoader.getResourceAsStream("resource.txt")(前面的/)。


使用MyClass.class.getClassLoader().getResourceAsStream(path)加载与代码相关联的资源。将MyClass.class.getResourceAsStream(path)用作快捷方式,并用于类包中打包的资源。

使用Thread.currentThread().getContextClassLoader().getResourceAsStream(path)获取属于客户机代码的资源,而不是与调用代码严格绑定的资源。您应该小心这一点,因为线程上下文类加载器可能指向任何东西。


普通旧Java 7上的纯Java,没有其他依赖性证明了差异。

我把file.txt放在c:\temp\中,把c:\temp\放在类路径上。

只有一种情况是两个调用之间存在差异。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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);
  }
}


这里的所有这些答案以及这个问题的答案都表明加载绝对URL,比如"foo/bar.properties"被class.getResourceAsStream(String)class.getClassLoader().getResourceAsStream(String)处理相同。但情况并非如此,至少在我的Tomcat配置/版本(目前为7.0.40)中没有。

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

对不起,我没有令人满意的解释,但我猜Tomcat会用下流的技巧和他在课堂上的黑色魔法,并造成不同。我过去一直使用class.getResourceAsStream(String),没有任何问题。

附:我也把这个贴在这里了


在尝试了一些加载文件但没有成功的方法之后,我记得我可以使用FileInputStream,这非常有效。

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

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


有效,试试这个:

1
InputStream in_s1 =   TopBrandData.class.getResourceAsStream("/assets/TopBrands.xml");