我试图在一个 webapp 中加载一个文件,当我使用 FileInputStream时,我得到了一个 FileNotFound异常。但是,使用相同的路径,我能够在执行 getResourceAsStream()时加载该文件。 这两种方法的区别是什么? 为什么一种方法有效,而另一种方法无效?
FileInputStream
FileNotFound
getResourceAsStream()
FileInputStream 将加载传递给构造函数的相对于 Java 进程的 工作目录的文件路径。通常在 web 容器中,这类似于 bin文件夹。
bin
getResourceAsStream()将加载相对于 从应用程序的类路径的文件路径。
FileInputStream类直接与底层文件系统一起工作。如果有问题的文件实际上不在那里,它将无法打开它。getResourceAsStream()方法的工作方式不同。它尝试使用调用它的类的 ClassLoader来定位和加载资源。这使它能够查找,例如,嵌入到 jar文件中的资源。
ClassLoader
jar
GetResourceAsStream ()通过 classname 的类加载器加载文件。如果类来自 jar 文件,那么将从该文件加载资源。
FileInputStream 用于从文件系统读取文件。
对于 web 应用程序来说,getResourceAsStream是正确的方法(正如你已经学到的那样)。
getResourceAsStream
原因是,如果您将 Web 应用程序打包在 WAR 中,那么从文件系统读取不能工作。这是打包 web 应用程序的正确方法。这种方式是可移植的,因为您不依赖于绝对文件路径或应用程序服务器的安装位置。
java.io.File和协作作用于本地磁盘文件系统。问题的根源在于 java.io中的 亲戚路径依赖于当前的工作目录。例如,JVM (在您的示例中是 webserver 的一个)从其中启动的目录。这可能是例如 C:\Tomcat\bin或完全不同的东西,但因此 没有 C:\Tomcat\webapps\contextname或任何你希望它是。在一个正常的 Eclipse 项目中,这将是 C:\Eclipse\workspace\projectname。你可透过以下途径了解现时的工作目录:
java.io.File
java.io
C:\Tomcat\bin
C:\Tomcat\webapps\contextname
C:\Eclipse\workspace\projectname
System.out.println(new File(".").getAbsolutePath());
然而,这个工作目录绝对不是可编程控制的。您真的应该更喜欢在 File API 中使用 绝对的路径,而不是相对路径。例如 C:\full\path\to\file.ext。
File
C:\full\path\to\file.ext
在 Java (web)应用程序中,您不希望硬编码或猜测绝对路径。这只是可移植性问题(即它在系统 X 中运行,但不在系统 Y 中运行)。通常的做法是将这类资源放置在 类路径中,或者将其完整路径添加到类路径(在 Eclipse 这样的 IDE 中,分别是 src文件夹和“构建路径”)。这样,你可以抓住他们的帮助下,由 ClassLoader#getResource()或 ClassLoader#getResourceAsStream()的 ClassLoader。它能够定位相对于类路径的“根”的文件,正如您巧合发现的那样。在 web 应用程序(或任何其他使用多个类加载器的应用程序)中,建议使用 Thread.currentThread().getContextClassLoader()返回的 ClassLoader,这样您也可以查看“外部”的 webapp 上下文。
src
ClassLoader#getResource()
ClassLoader#getResourceAsStream()
Thread.currentThread().getContextClassLoader()
在网络应用程序的另一种选择是 ServletContext#getResource()和它的对应物 ServletContext#getResourceAsStream()。它能够访问位于 webapp 项目的公共 web文件夹中的文件,包括 /WEB-INF文件夹。通过继承的 getServletContext()方法,可以在 servlet 中使用 ServletContext,您可以按原样调用它。
ServletContext#getResource()
ServletContext#getResourceAsStream()
web
/WEB-INF
getServletContext()
ServletContext
在这里,我通过将它们标记为 File Read (java.io)和 Resource Read (ClassLoader.getResourceAsStream ())来分离这两种用法。
档案读取 - 1. 在本地文件系统上工作。 2. 尝试将从当前 JVM 启动的目录中请求的文件定位为 root 用户 3. 在预先确定的位置(如/dev/files 或 C: Data)中使用文件进行处理时,最好是这样。
资源阅读 - 1. 在类路径上工作 2. 尝试在当前或父类加载器类路径中定位文件/资源。 3. 在试图从 war 或 jar 这样的打包文件中加载文件时,最好是这样。