I was trying to load a file in a webapp, and I was getting a FileNotFound exception when I used FileInputStream. However, using the same path, I was able to load the file when I did getResourceAsStream().
What is the difference between the two methods, and why does one work while the other doesn't?
The java.io.File and consorts acts on the local disk file system. The root cause of your problem is that relative paths in java.io are dependent on the current working directory. I.e. the directory from which the JVM (in your case: the webserver's one) is started. This may for example be C:\Tomcat\bin or something entirely different, but thus not C:\Tomcat\webapps\contextname or whatever you'd expect it to be. In a normal Eclipse project, that would be C:\Eclipse\workspace\projectname. You can learn about the current working directory the following way:
System.out.println(new File(".").getAbsolutePath());
However, the working directory is in no way programmatically controllable. You should really prefer using absolute paths in the File API instead of relative paths. E.g. C:\full\path\to\file.ext.
You don't want to hardcode or guess the absolute path in Java (web)applications. That's only portability trouble (i.e. it runs in system X, but not in system Y). The normal practice is to place those kind of resources in the classpath, or to add its full path to the classpath (in an IDE like Eclipse that's the src folder and the "build path" respectively). This way you can grab them with help of the ClassLoader by ClassLoader#getResource() or ClassLoader#getResourceAsStream(). It is able to locate files relative to the "root" of the classpath, as you by coincidence figured out. In webapplications (or any other application which uses multiple classloaders) it's recommend to use the ClassLoader as returned by Thread.currentThread().getContextClassLoader() for this so you can look "outside" the webapp context as well.
Another alternative in webapps is the ServletContext#getResource() and its counterpart ServletContext#getResourceAsStream(). It is able to access files located in the public web folder of the webapp project, including the /WEB-INF folder. The ServletContext is available in servlets by the inherited getServletContext() method, you can call it as-is.
See also:
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
getResourceAsStream is the right way to do it for web apps (as you already learned).
The reason is that reading from the file system cannot work if you package your web app in a WAR. This is the proper way to package a web app. It's portable that way, because you aren't dependent on an absolute file path or the location where your app server is installed.
FileInputStream will load a the file path you pass to the constructor as relative from the working directory of the Java process. Usually in a web container, this is something like the bin folder.
getResourceAsStream() will load a file path relative from your application's classpath.
The FileInputStream class works directly with the underlying file system. If the file in question is not physically present there, it will fail to open it. The getResourceAsStream() method works differently. It tries to locate and load the resource using the ClassLoader of the class it is called on. This enables it to find, for example, resources embedded into jar files.
classname.getResourceAsStream() loads a file via the classloader of classname. If the class came from a jar file, that is where the resource will be loaded from.
FileInputStream is used to read a file from the filesystem.
I am here by separating both the usages by marking them as File Read(java.io) and Resource Read(ClassLoader.getResourceAsStream()).
File Read -
1. Works on local file system.
2. Tries to locate the file requested from current JVM launched directory as root
3. Ideally good when using files for processing in a pre-determined location like,/dev/files or C:\Data.
Resource Read -
1. Works on class path
2. Tries to locate the file/resource in current or parent classloader classpath.
3. Ideally good when trying to load files from packaged files like war or jar.
Related
I'm trying to develop a cross-platform application that works on Desktop and Android as well using JavaFX and Gluon.
At runtime my code creates a serialized file in my resource folder. I also need to read and write serialized data from/to it.
I managed to work it on desktop, but not on android. Because it have a different file structure I guess.
That is why I try to get the file path dynamically.
Existing resource files, which are created before runtime (and not modified) seems to works fine on both platform.
I tried with new File("src/main/resources/folder/file.ser").getAbsolutePath(); and by trying to access it from my root folder like this: getClass.getResources("/folder/file.ser").getPath();. Both of them works fine on desktop (Windows) but unfortunately Android does not find the file by file path.
An other problem could be that I should not create runtime files in the resource folder but then where should I?
Any idea how can I read and write runtime created files that works both on android and desktop?
(If the information is not enough to help me, I try to reproduce my code in a minimal form and provide further details.)
I think you are on a completely wrong track. Creating or writing to files in the resource folder does not work in general. The idea is that files in the resource folder get packaged into jar files or are otherwise bundled with an application and are not writable at runtime.
What you should do is to create an application folder when your program is launched for the first time. A common practice on desktop is for example to create an invisible folder ".myApp" in the users home directory. On other platforms like Android there are other platform specific naming and location rules, but the concept is the same. At first launch time you can also copy necessary resources from your resource folder into this application folder so that you can edit them at runtime.
Resource files with a path on the class path, could be packed in a jar and should be considered read-only, especially as resources might be cached in some cases. They are not File. They can be captured by URL, URI, Path. The paths are case-sensitive and the path separator is /.
Hence resources can only be used as a template, an initial file. They should be copied to a real File, outside the application.
Path path = Paths.get(System.getProperty("user.home"), ".myapp/file.ser");
Files.createDirectories(path.getParent());
if (Files.exists(path)) {
URL url = MyClass.class.getResource("/folder/file.ser");
Path template = Paths.get(url.toURI());
Files.copy(template, path);
}
Furthermore .ser, a serialized java object, is not a good idea. I would suggest XML
using JAXB with annotations. More readable, maintainable, versionable. No clash between development JRE at your place and deployed JRE at the client.
I was trying to load a file in a webapp, and I was getting a FileNotFound exception when I used FileInputStream. However, using the same path, I was able to load the file when I did getResourceAsStream().
What is the difference between the two methods, and why does one work while the other doesn't?
The java.io.File and consorts acts on the local disk file system. The root cause of your problem is that relative paths in java.io are dependent on the current working directory. I.e. the directory from which the JVM (in your case: the webserver's one) is started. This may for example be C:\Tomcat\bin or something entirely different, but thus not C:\Tomcat\webapps\contextname or whatever you'd expect it to be. In a normal Eclipse project, that would be C:\Eclipse\workspace\projectname. You can learn about the current working directory the following way:
System.out.println(new File(".").getAbsolutePath());
However, the working directory is in no way programmatically controllable. You should really prefer using absolute paths in the File API instead of relative paths. E.g. C:\full\path\to\file.ext.
You don't want to hardcode or guess the absolute path in Java (web)applications. That's only portability trouble (i.e. it runs in system X, but not in system Y). The normal practice is to place those kind of resources in the classpath, or to add its full path to the classpath (in an IDE like Eclipse that's the src folder and the "build path" respectively). This way you can grab them with help of the ClassLoader by ClassLoader#getResource() or ClassLoader#getResourceAsStream(). It is able to locate files relative to the "root" of the classpath, as you by coincidence figured out. In webapplications (or any other application which uses multiple classloaders) it's recommend to use the ClassLoader as returned by Thread.currentThread().getContextClassLoader() for this so you can look "outside" the webapp context as well.
Another alternative in webapps is the ServletContext#getResource() and its counterpart ServletContext#getResourceAsStream(). It is able to access files located in the public web folder of the webapp project, including the /WEB-INF folder. The ServletContext is available in servlets by the inherited getServletContext() method, you can call it as-is.
See also:
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
getResourceAsStream is the right way to do it for web apps (as you already learned).
The reason is that reading from the file system cannot work if you package your web app in a WAR. This is the proper way to package a web app. It's portable that way, because you aren't dependent on an absolute file path or the location where your app server is installed.
FileInputStream will load a the file path you pass to the constructor as relative from the working directory of the Java process. Usually in a web container, this is something like the bin folder.
getResourceAsStream() will load a file path relative from your application's classpath.
The FileInputStream class works directly with the underlying file system. If the file in question is not physically present there, it will fail to open it. The getResourceAsStream() method works differently. It tries to locate and load the resource using the ClassLoader of the class it is called on. This enables it to find, for example, resources embedded into jar files.
classname.getResourceAsStream() loads a file via the classloader of classname. If the class came from a jar file, that is where the resource will be loaded from.
FileInputStream is used to read a file from the filesystem.
I am here by separating both the usages by marking them as File Read(java.io) and Resource Read(ClassLoader.getResourceAsStream()).
File Read -
1. Works on local file system.
2. Tries to locate the file requested from current JVM launched directory as root
3. Ideally good when using files for processing in a pre-determined location like,/dev/files or C:\Data.
Resource Read -
1. Works on class path
2. Tries to locate the file/resource in current or parent classloader classpath.
3. Ideally good when trying to load files from packaged files like war or jar.
i hv created the .jar file by building my project in netbeans.
The .jar file exist in "dist/myproject.jar". But when i move it to
any other system will it find the paths specified for images etc in
project?
As i am giving the paths like
(C:/Users/Lucky/Documents/NetBeansProjects/CoverageAnalyzer/src/coverageanalyzer/icons/icon.png).
OR When i write path just to approach root directory as
(icons/icon.png), so then also?
Summary: What is the actual way that i can write/copy my .jar file to
any other system without spoiling the paths and program run correctly
on any other system. Thanks in advance.help will be appriciated
You can use one of the following approaches:
Use relative paths (relative to the executable file location) instead of absolute paths.
Use paths under a known absolute path (an installation folder, path from environment variable / configuration file / registry key, the user's directory (user.home) etc.)
Use resources embeded into your jar. See Class.getResource() and Class.getResourceAsStream()
Note that you should consider cross-platform (Windows/Linux/Mac) and resource hiding issues when selecting a suitable approach.
The solution is to put the auxiliary files as resources into to jar. The program must then load them from the class path instead of the file system.
Have a look at Class.getResourceAsStream.
I am reading the documentation for the getRealPath(String s) and I became confused when I read this statement
Returns a String containing the real path for a given virtual path. For example, the path /index.html returns the absolute file path on the server's filesystem would be served by a request for http://host/contextPath/index.html, where contextPath is the context path of this ServletContext..
What is a virtual path? let's say I am inside in my deployment environment and when I say getRealPath("index.html") does this usually points to the WEB-INF directory? or does getRealPath() starts reading the root directory(inside the folder name of the web app)?
The paths of resources you access from within the web container are all virtual paths. Their virtual root is the base folder of the web application. But in the actual operating system where they reside, they will be in a "concrete" location for sure. getRealPath gives you that path.
Say you have a jsp named index.jsp:
Take note that whenever you use this, you're creating cracks on the portability of your web application.
ServletContext.getRealPath(String s) returns the real file system path. The input string is interpreted relative your Web Applications' Context Path.
In e.g. eclipse this is typically the folder WebContent inside your Web Application project (it is possible to customize that). After building a WAR file out of the project, you'll realize that the WebContent folder disappeared, so on the server the input string is interpreted relative to the WAR files' folder (or the .war library - this depends whether you explode the WAR during deployment or not).
A note on security
This method should (not must) only be used in case you want to access a file on the serverside. Typical scenario is parsing a configuration file during startup. Just keep in mind to never let the caller of your application know the real file system path of a given resource.
So this might be a stupid question but...
I want to package a specific WSDL file in with an EJB project within eclipse. What would be the best way to refer to this file in my code?
I would like to use a relative path but the current directory starts off in the /bin directory of my JBOSS installation. It seems like there should be a way to refer to the file in relation to the project file structure.
Any ideas?
getClass().getResource(String path) uses a relative path to locate a classpath resource. It returns a java.net.URL. Alternatively, you can use getResourceAsStream(..) to obtain the InputStream to the resource.