ChatGPT解决这个技术问题 Extra ChatGPT

getResourceAsStream() 与 FileInputStream

我试图在 web 应用程序中加载文件,但在使用 FileInputStream 时出现 FileNotFound 异常。但是,使用相同的路径,当我执行 getResourceAsStream() 时,我能够加载文件。这两种方法有什么区别,为什么一种有效而另一种无效?


C
Community

java.io.File 和 consorts 作用于本地磁盘文件系统。问题的根本原因是 java.io 中的 relative 路径依赖于当前工作目录。即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 中)。通常的做法是将这些资源放在 类路径 中,或者将其完整路径添加到类路径中(在像 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 中可用,您可以按原样调用它。

也可以看看:

在基于 servlet 的应用程序中放置以及如何读取配置资源文件?

servletcontext.getRealPath("/") 是什么意思,我应该什么时候使用它

在 servlet 应用程序中保存上传文件的推荐方法

如何在基于 servlet 的 Web 应用程序中临时保存生成的文件


A
Ajinkya

getResourceAsStream 是为网络应用程序执行此操作的正确方法(正如您已经了解的那样)。

原因是如果您将 Web 应用程序打包到 WAR 中,则无法从文件系统读取。这是打包 Web 应用程序的正确方法。这种方式是可移植的,因为您不依赖于绝对文件路径或应用服务器的安装位置。


+1 - 虽然“不能工作”太强了。 (可以使从文件系统读取工作,但是可移植地进行读取是一个棘手的问题......还有更多代码,尤其是如果资源在 JAR 中。)
duffy,非常好的答案,您解释了我的错误是什么,但是 BalusC 详细介绍了很多细节 - 我认为他的回答对那些想了解内部细节的人也有帮助。希望您不介意我将接受的答案更改为他的答案!
@Stephen - 我不认为“不能工作”太强了。即使像部署在具有不同应用服务器路径的两台不同服务器上这样简单的事情也会破坏它。关键是您需要使您的 WAR 尽可能独立。你的观点是正确的,但我会坚持我的措辞。
I
Iswanto San

FileInputStream 将加载您传递给构造函数的文件路径作为相对于 Java 进程的工作目录。通常在 Web 容器中,这类似于 bin 文件夹。

getResourceAsStream() 将加载相对于 from your application's classpath 的文件路径。


D
Dirk

FileInputStream 类直接使用底层文件系统。如果有问题的文件实际上不存在于那里,它将无法打开它。 getResourceAsStream() 方法的工作方式不同。它尝试使用调用它的类的 ClassLoader 来定位和加载资源。例如,这使其能够查找嵌入到 jar 文件中的资源。


好吧,jar 中的文件仍然物理“存在”在文件系统中,只是包含在其他文件中
嗯,是的,当然。但它们通常不被视为文件系统中的独立实体,除非您的应用程序碰巧知道 jar 文件格式及其含义。在 Java 中,适当的 ClassLoader 可能具有这种知识,而普通的 FileInputStream 肯定没有。
L
Lachlan Roche

classname.getResourceAsStream() 通过类名的类加载器加载文件。如果该类来自一个 jar 文件,那么这就是加载资源的地方。

FileInputStream 用于从文件系统中读取文件。


A
Aditya Bhuyan

我在这里通过将它们标记为文件读取(java.io)和资源读取(ClassLoader.getResourceAsStream())来分离这两种用法。

文件读取 - 1. 适用于本地文件系统。 2. 尝试将当前 JVM 启动目录请求的文件定位为 root 3. 理想情况下使用文件在预先确定的位置(如 /dev/files 或 C:\Data)进行处理。

资源读取 - 1. 在类路径上工作 2. 尝试在当前或父类加载器类路径中定位文件/资源。 3. 尝试从打包文件(如 war 或 jar)加载文件时非常理想。