satoshi
satoshi

Reputation: 4113

Accessing files/directories in webapp folder in Spring

I'm inexperienced in Spring and everything I need to do now is to access and obtain a reference to files and folders in the webapp folder. Here is my relevant project hierarchy:

-src
--main
---java (marked as source root)
----my
-----package
------controller
-------Home.java
---webapp
----images
-----avatars

My code in Home.java:

@Controller
@RequestMapping("/")
public class Home
{
    @RequestMapping(method = RequestMethod.GET)
    public String index(Model model,
                        HttpServletRequest request) throws Exception
    {
        String test1 = request.getSession().getServletContext().getRealPath("");
        String test2 = request.getSession().getServletContext().getRealPath("/");
        String test3 = request.getRealPath("");
        String test4 = request.getRealPath("/");
        String test5 = request.getSession().getServletContext().getRealPath(request.getServletPath());

        return "index";
    }
}

All the 5 requests return null. Am I doing something wrong?

web.xml:

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
         http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         version="2.5">

    <display-name>Test</display-name>

    <servlet>
        <servlet-name>test</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>test</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/applicationContext.xml</param-value>
    </context-param>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <session-config>
        <session-timeout>10</session-timeout>
    </session-config>
</web-app>

What I'm trying to achieve (not shown in this example), is to code a controller responsible for the scaling of an image. For this reason I would need access to the src/main/webapp/_images folder.

Thank you!

Update: simplified the example for better understanding.

Update2: thanks to the suggestion of @gigadot, I deployed the application as an exploded WAR and the problem is partly solved. Can someone tell me what's the difference in deploying the WAR as exploded? Is it something not recommended to do on a production server? Advantages/disadvantages?

I think it's worth to explain the situation with an example. Let's say I'm coding a social network and I have to possibility to upload my personal profile picture. This picture will be uploaded to the src/main/webapp/_images/avatars/[myid].jpg folder. Is it recommended to upload pictures to the webapp folder? Or is there a better solution? I would like to be able to return a scaled instance of the image when accessing the URL /images/[width]x[height]/[userid].jpg.

Deploying the WAR as exploded and implementing the ResourceLoaderAware (thanks @KevinSchmidt), I can make it work using this:

resourceLoader.getResource("file:" + request.getSession().getServletContext().getRealPath("/") + "_images/avatars/");

To me it looks quite dirty, is it a good idea for a production server? Is there a better solution?

Upvotes: 16

Views: 79184

Answers (6)

gigadot
gigadot

Reputation: 8969

How exactly did you deploy your application?

ServletContext().getRealPath("/") may return null if it is not deployed as exploded. Read the link below for further information. However, the method to configure this may not be the same for your servlet container.

http://ananthkannan.blogspot.com/2009/12/servletcontextgetrealpath-returns-null.html

Updates

Can someone tell me what's the difference in deploying the WAR as exploded?

When you deploy the war file as exploded, the servlet container, e.g. Tomcat, will extract the content of war file into a temporary folder and runs everything from that folder so the {WEB_ROOT]/_images/avatars/[myid].jpg is actually exist on file system (hard disk). Therefore, you can actually get the real path (as it already says in the name of the method). However, if your servlet container does not extract the war file, the folder you are looking for is inside the war file and there is no real path to it so it will return null.

Is it something not recommended to do on a production server? Advantages/disadvantages?

You should not store dynamic contents under your source folder or the webroot folder (webapp) since servlet container will use it temporarily and delete it or change to a new folder when you redeploy your web application. You will likely lost the dynamic data you put into these folders. The web root folder is usually designed for storing static content, which means content you don't want to change - for example, graphic images for your web component like background images, css, etc.

The usual method for storing user data is to somehow create a folder in userspace and put your dynamic data in there. However, you will not be able to serve the content of the folder outside webroot. You will need to write your own static servlet to pipe the data when they are requested. This is quite complicated for a beginner.

The easiest way for implementing your own static servlet to serve dynamic content is to extend the static servlet of your servlet container. However, your code will highly depend on the servlet container you are deploying to.

Since you are going to provide a REST interface for resizing images, you can create a controller which reads in the original images from the dynamic content folder, do the resizing, save it as a temporary file or flush the content of the HttpResponse.

Upvotes: 16

Kevin Schmidt
Kevin Schmidt

Reputation: 2261

Considering the location of the file, you should look into the Spring ResourceLoader, like this:

public class ArbitraryResourceLoader implements ResourceLoaderAware {
    private ResourceLoader resourceLoader;

    public void test() {
        Resource resource = resourceLoader.getResource("file:webapp/images/avatars/avatar.jpg");
    }

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }
}

Please be aware that your container needs to have permission to access the file.

Upvotes: 4

horaceman
horaceman

Reputation: 375

you can try this:

@Controller
@RequestMapping("/")
public class Home implements ServletContextAware
{
    private ServletContext servletContext;
    public void setServletContext(ServletContext servletCtx){
       this.servletContext=servletCtx;
    }

    @RequestMapping(method=RequestMethod.GET)
    public String index(Model model,HttpServletRequest) throws Exception{
       String rootPath = servletContext.getRealPath("/");
       //...
    }
}

Upvotes: 0

This is my code and it works I dont get null.

I'm running JDK 1.7_02 Tomcat 7.0.25 Spring 3.1.1

And i'm not getting nulls

    @Controller
    public class IndexController {

        @RequestMapping("/index")
        public String go(HttpServletRequest request, HttpServletResponse response) {
            HttpSession session = request.getSession();
            ServletContext sc = session.getServletContext();
            String x = sc.getRealPath("/");
            System.out.println(x);
            return "index";
        }
    }

Upvotes: 4

Paul
Paul

Reputation: 20091

Take a look at using <mvc:resources> in the Spring Reference, section 16.14.5 Configuring Serving of Resources. I use this method for my JavaScript/CSS/images and it works great - I just had to add a single line to my application-context.xml file. The documentation states:

This provides a convenient way to serve static resources from locations other than the web application root, including locations on the classpath.

Upvotes: 0

Alessandro Santini
Alessandro Santini

Reputation: 2003

ClassPathResource might be of help instead. Your way of accessing a resource in the classpath if flatly wrong.

For what concerns the location of web resources, those stored in the src/main/resources would be deployed in the WEB-INF/classes folder, so it is clearly a no go. User webapp instead.

Update: now I see where you are coming from. I do not see anything wrong with the mapping, although extremely exotic. Chances are that the DispatcherServlet mapping does not cover the images folder (i.e. it is not /*).

For what concerns the File opening, the webapp directory is not visible from the class loader with the only exception of the WEB-INF folder. As such, you will need to invoke the HttpServletRequest.getRealPath() method in order to get the real path of the image.

Please note that, although unlikely to be your case, hardened Java JVMs (i.e. JVMs with security policies in place) may require extra grants to let the servlet open a file from an arbitrary location.

Upvotes: 0

Related Questions