getResourceAsStream() 与 FileInputStream
Posted
技术标签:
【中文标题】getResourceAsStream() 与 FileInputStream【英文标题】:getResourceAsStream() vs FileInputStream 【发布时间】:2011-01-19 11:22:47 【问题描述】:我试图在 web 应用程序中加载文件,当我使用 FileInputStream
时出现 FileNotFound
异常。但是,使用相同的路径,当我执行getResourceAsStream()
时,我能够加载文件。
这两种方法有什么区别,为什么一种有效而另一种无效?
【问题讨论】:
【参考方案1】:java.io.File
和 consorts 作用于本地磁盘文件系统。问题的根本原因是 java.io
中的 relative 路径依赖于当前工作目录。 IE。 JVM(在您的情况下:网络服务器的)启动的目录。例如,这可能是C:\Tomcat\bin
或完全不同的东西,但因此不是 C:\Tomcat\webapps\contextname
或任何您期望的。在一个普通的 Eclipse 项目中,这将是 C:\Eclipse\workspace\projectname
。您可以通过以下方式了解当前工作目录:
System.out.println(new File(".").getAbsolutePath());
但是,工作目录绝不是可编程控制的。您应该更喜欢在File
API 中使用 absolute 路径而不是相对路径。例如。 C:\full\path\to\file.ext
.
您不想硬编码或猜测 Java(Web)应用程序中的绝对路径。这只是可移植性问题(即它在系统 X 中运行,但不在系统 Y 中)。通常的做法是将这些资源放在 classpath 中,或者将其完整路径添加到 classpath(在像 Eclipse 这样的 IDE 中,分别是 src
文件夹和“构建路径”) .这样您就可以在ClassLoader#getResource()
或ClassLoader#getResourceAsStream()
的ClassLoader
的帮助下获取它们。正如您碰巧发现的那样,它能够定位相对于类路径的“根”的文件。在 web 应用程序(或任何其他使用多个类加载器的应用程序)中,建议使用Thread.currentThread().getContextClassLoader()
返回的 ClassLoader
,这样您也可以在 webapp 上下文“外部”查看。
webapps 中的另一个替代方案是ServletContext#getResource()
及其对应的ServletContext#getResourceAsStream()
。它能够访问位于 webapp 项目的公共 web
文件夹中的文件,包括 /WEB-INF
文件夹。 ServletContext
可通过继承的getServletContext()
方法在servlet 中使用,您可以按原样调用它。
另见:
Where to place and how to read configuration resource files in servlet based application? What does servletcontext.getRealPath("/") mean and when should I use it Recommended way to save uploaded files in a servlet application How to save generated file temporarily in servlet based web application【讨论】:
@khylo:相关:***.com/questions/7952090/…【参考方案2】:getResourceAsStream
是为网络应用程序执行此操作的正确方法(正如您已经了解的那样)。
原因是如果您将 Web 应用程序打包到 WAR 中,则无法从文件系统读取。这是打包 Web 应用程序的正确方法。它是可移植的,因为您不依赖于绝对文件路径或应用服务器的安装位置。
【讨论】:
+1 - 虽然“不能工作”太强了。 (可以从文件系统中读取数据,但可移植地进行读取是一个棘手的问题……而且代码更多,尤其是在资源位于 JAR 中的情况下。) duffy,非常好的答案,你解释了我的错误是什么,但 BalusC 详细介绍了很多细节 - 我认为他的回答对那些想了解内部细节的人也有帮助.希望您不介意我将接受的答案更改为他的答案! @Stephen - 我不认为“不能工作”太强了。即使是简单的部署在两个不同的服务器上,使用不同的应用服务器路径也会破坏它。关键是您需要使您的 WAR 尽可能独立。你的观点是正确的,但我会坚持我的措辞。【参考方案3】:FileInputStream 将加载您传递给构造函数的文件路径,作为相对于Java 进程的工作目录 的路径。通常在 Web 容器中,这类似于 bin
文件夹。
getResourceAsStream()
将加载相对于from your application's classpath 的文件路径。
【讨论】:
【参考方案4】:FileInputStream
类直接与底层文件系统一起工作。如果有问题的文件实际上不存在于那里,它将无法打开它。 getResourceAsStream()
方法的工作方式不同。它尝试使用调用它的类的ClassLoader
来定位和加载资源。例如,这使其能够查找嵌入到 jar
文件中的资源。
【讨论】:
嗯,jar 中的文件仍然物理“存在”在文件系统中,只是包含在其他文件中 嗯,是的,当然。但它们通常不被视为文件系统中的独立实体,除非您的应用程序碰巧知道jar
文件格式及其含义。而在 Java 中,适当的 ClassLoader
可能具有这种知识,而普通的 FileInputStream
肯定没有。【参考方案5】:
classname.getResourceAsStream() 通过类名的类加载器加载文件。如果该类来自一个 jar 文件,那么资源将从那里加载。
FileInputStream 用于从文件系统中读取文件。
【讨论】:
【参考方案6】:我在这里通过将两种用法标记为 File Read(java.io) 和 Resource Read(ClassLoader.getResourceAsStream()) 来分隔这两种用法。
文件读取 - 1.适用于本地文件系统。 2.尝试以root身份定位从当前JVM启动目录请求的文件 3. 理想情况下使用文件在预先确定的位置(如 /dev/files 或 C:\Data)进行处理。
资源读取 - 1.在类路径上工作 2. 尝试在当前或父类加载器类路径中定位文件/资源。 3. 尝试从打包文件(如 war 或 jar)加载文件时非常理想。
【讨论】:
以上是关于getResourceAsStream() 与 FileInputStream的主要内容,如果未能解决你的问题,请参考以下文章
ClassLoader.getResourceAsStream() 与 Class.getResourceAsStream()的区别
getResourceAsStream() 与 FileInputStream
this.class.getClassLoader().getResourceAsStream与this.class.getResourceAsStream
getResourceAsStream()与FileInputStream
为啥 getResourceAsStream() 可以在 IDE 中工作,但不能在 JAR 中工作?
className.class.getResourceAsStream与ClassLoader.getSystemResourceAsStream区别