我试图在 web 应用程序中加载文件,但在使用 FileInputStream
时出现 FileNotFound
异常。但是,使用相同的路径,当我执行 getResourceAsStream()
时,我能够加载文件。这两种方法有什么区别,为什么一种有效而另一种无效?
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 应用程序中临时保存生成的文件
getResourceAsStream
是为网络应用程序执行此操作的正确方法(正如您已经了解的那样)。
原因是如果您将 Web 应用程序打包到 WAR 中,则无法从文件系统读取。这是打包 Web 应用程序的正确方法。这种方式是可移植的,因为您不依赖于绝对文件路径或应用服务器的安装位置。
FileInputStream 将加载您传递给构造函数的文件路径作为相对于 Java 进程的工作目录。通常在 Web 容器中,这类似于 bin
文件夹。
getResourceAsStream()
将加载相对于 from your application's classpath 的文件路径。
FileInputStream
类直接使用底层文件系统。如果有问题的文件实际上不存在于那里,它将无法打开它。 getResourceAsStream()
方法的工作方式不同。它尝试使用调用它的类的 ClassLoader
来定位和加载资源。例如,这使其能够查找嵌入到 jar
文件中的资源。
jar
文件格式及其含义。在 Java 中,适当的 ClassLoader
可能具有这种知识,而普通的 FileInputStream
肯定没有。
classname.getResourceAsStream() 通过类名的类加载器加载文件。如果该类来自一个 jar 文件,那么这就是加载资源的地方。
FileInputStream 用于从文件系统中读取文件。
我在这里通过将它们标记为文件读取(java.io)和资源读取(ClassLoader.getResourceAsStream())来分离这两种用法。
文件读取 - 1. 适用于本地文件系统。 2. 尝试将当前 JVM 启动目录请求的文件定位为 root 3. 理想情况下使用文件在预先确定的位置(如 /dev/files 或 C:\Data)进行处理。
资源读取 - 1. 在类路径上工作 2. 尝试在当前或父类加载器类路径中定位文件/资源。 3. 尝试从打包文件(如 war 或 jar)加载文件时非常理想。
不定期副业成功案例分享