What does servletcontext.getRealPath(/) mean and when should I use it
Introduction
The ServletContext#getRealPath()
is intented to convert a web content path (the path in the expanded WAR folder structure on the server's disk file system) to an absolute disk file system path.
The "/"
represents the web content root. I.e. it represents the web
folder as in the below project structure:
YourWebProject
|-- src
| :
|
|-- web
| |-- META-INF
| | `-- MANIFEST.MF
| |-- WEB-INF
| | `-- web.xml
| |-- index.jsp
| `-- login.jsp
:
So, passing the "/"
to getRealPath()
would return you the absolute disk file system path of the /web
folder of the expanded WAR file of the project. Something like /path/to/server/work/folder/some.war/
which you should be able to further use in File
or FileInputStream
.
Note that most starters don't seem to see/realize that you can actually pass the whole web content path to it and that they often use
String absolutePathToIndexJSP = servletContext.getRealPath("/") + "index.jsp"; // Wrong!
or even
String absolutePathToIndexJSP = servletContext.getRealPath("") + "index.jsp"; // Wronger!
instead of
String absolutePathToIndexJSP = servletContext.getRealPath("/index.jsp"); // Right!
Don't ever write files in there
Also note that even though you can write new files into it using FileOutputStream
, all changes (e.g. new files or edited files) will get lost whenever the WAR is redeployed; with the simple reason that all those changes are not contained in the original WAR file. So all starters who are attempting to save uploaded files in there are doing it wrong.
Moreover, getRealPath()
will always return null
or a completely unexpected path when the server isn't configured to expand the WAR file into the disk file system, but instead into e.g. memory as a virtual file system.
getRealPath()
is unportable; you'd better never use it
Use getRealPath()
carefully. There are actually no sensible real world use cases for it. Based on my 20 years of Java EE experience, there has always been another way which is much better and more portable than getRealPath()
.
If all you actually need is to get an InputStream
of the web resource, better use ServletContext#getResourceAsStream()
instead, this will work regardless of the way how the WAR is expanded. So, if you for example want an InputStream
of index.jsp
, then do not do:
InputStream input = new FileInputStream(servletContext.getRealPath("/index.jsp")); // Wrong!
But instead do:
InputStream input = servletContext.getResourceAsStream("/index.jsp"); // Right!
Or if you intend to obtain a list of all available web resource paths, use ServletContext#getResourcePaths()
instead.
Set<String> resourcePaths = servletContext.getResourcePaths("/");
You can obtain an individual resource as URL
via ServletContext#getResource()
. This will return null
when the resource does not exist.
URL resource = servletContext.getResource(path);
Or if you intend to save an uploaded file, or create a temporary file, then see the below "See also" links.
See also:
- getResourceAsStream() vs FileInputStream
- Recommended way to save uploaded files in a servlet application
- Simple ways to keep data on redeployment of Java EE 7 web application
ServletContext getRealPath method - what is a virtual path?
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.
ServletContext().getRealPath() returns path not ending with /
I Fixed the issue.
The issue was, I didn't put '/' before "resources".
ServletContext.getRealPath("")+"/resources"
Context getRealPath() for non-existent file
I have taken the time to dive into Tomcat source to find the cause for this. It turns out that getRealPath
, in addition to retrieving the system path for a given virtual path, also works a bit with the Tomcat cache.
NOTE:
I know that my file separator usage is not good, but Tomcat is smart enough to validate the above call to produce /bluesModified.wav
. So even if I call it like @rickz mentioned in the comments, the result would be the same and therefore that was not the issue.
The issues I had with being unable to reference the file in the case of the following call
context.getRealPath("/" + "\\bluesModified.wav")
was the fact that in this case we are passing the file path to the method, while in the case that works we are passing in the directory path.
What happens is that the call to getRealPath()
first checks the cache for the existence of the resource identified by the webapppath /bluesModified.wav
. Since it does not exist at the moment of the call, Tomcat will create an instance of the EmptyResource
class which is basically a wrapper around File
class and represents a file that does not exist, and it will then store the reference to this file in its cache.
The issue here is that even though I create a file that will have the correct virtual path Tomcat will still have that empty resource representing a non existent file in its cache. In other words, if I reference the file from the client side like so
http://localhost:8080/AudioSimulator/bluesModified.wav
Tomcat will return the cached resource that represents the empty file, which actually means a 404
to the client even though the file exists.
Waiting for 5 seconds, which is the time to live of Tomcat cache entries, and then trying to reference the file will revalidate the cache entry and produce a FileResource
instead of EmptyResource
in which case the referencing will work normally.
It works in this case
context.getRealPath("/") + "\\bluesModified.wav"
since the path that is getting cached is a directory and the file name is simply concatenated. So the string I have here is just an absolute path to the file I am going to create with no cache entries colliding with it.
My mistake was assuming that getRealPath()
is just some "pure" method that will return a string I can use to create files while in fact it has a bit of side effects. These side effects are not documented and even though I might have done some things incorrectly the bottom line is this method is not that predictable to use when doing File IO stuff.
Does ServletContext.getRealPath() work with web fragments?
Yes, getResourcesAsStream()
always works. But getRealPath()
doesn't.
Answer: Don't try to use getRealPath()
together with web fragments.
How do I get real path as string in Grails
I was unable to find a solution that worked both locally and when deployed in a container. I ended up using this workaround instead to get the path depending on which environment the code is running in:
def path
if (Environment.current == Environment.DEVELOPMENT) {
path = java.nio.file.FileSystems.default.getPath("someSource").toAbsolutePath().toString()
} else {
path = ServletContextHolder.servletContext.getRealPath("/someSource")
}
Related Topics
Java Https Client Certificate Authentication
How Many Threads Can a Java Vm Support
Servlet For Serving Static Content
How to Find a Button Source in Awt (Calculator Homework)
Getting a File'S Md5 Checksum in Java
Why Should the Interface For a Java Class Be Preferred
What Is the Ellipsis (...) For in This Method Signature
Catching Java.Lang.Outofmemoryerror
Resultset Exception - Before Start of Result Set
Socket Using in a Swing Applet
Why Is the Java Date API (Java.Util.Date, .Calendar) Such a Mess
Understanding Java'S Protected Modifier
Try/Catch With Inputmismatchexception Creates Infinite Loop
Get the Height of a Node in Javafx (Generate a Layout Pass)
What Does "Incompatible Types: Void Cannot Be Converted to ..." Mean