How to display uploaded images in Spring MVC application

16,798

Solution 1

You have to save your files into an external folder.

For example I create a directory like /home/webappFolder/ and inside it I create some other subdirectory. For example a repository directory where I store uploaded data.. a report subdirectory where I put my jasper report files.. etc

/home/webappFolder/repo
/home/webappFolder/report
/home/webappFolder/logs

As you can read in xml posted in your question:

<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->

in resources you serve up static resources.

Solution 2

Here is a ready-for-use images upload / download controller exactly for this purpose:

  1. First of all we need to upload images via a simple form (admin.jsp):

     <form method="POST" action="uploadFile" enctype="multipart/form-data">
        File to upload: <input type="file" name="file" >
        <br />
        Name: <input type="text" name="name" >
        <br />
        <br />
        <input type="submit" value="Upload">
    </form>
    <c:if test="${not empty message}">
        ${message} <!-- here would be a message with a result of processing -->
    </c:if>
    
  2. Now we need a controller, that can upload images to the server and show them later at jsp page:

    package com.pizza.controllers;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.multipart.MultipartFile;
    import org.springframework.web.servlet.ModelAndView;
    
    import java.io.BufferedOutputStream;
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.nio.file.Files;
    
    @Controller
    public class FileUploadController {
    
        private static final String PIZZA_IMAGES = "pizzaImages";
        private static final String TOMCAT_HOME_PROPERTY = "catalina.home";
        private static final String TOMCAT_HOME_PATH = System.getProperty(TOMCAT_HOME_PROPERTY);
        private static final String PIZZA_IMAGES_PATH = TOMCAT_HOME_PATH + File.separator + PIZZA_IMAGES;
    
        private static final File PIZZA_IMAGES_DIR = new File(PIZZA_IMAGES_PATH);
        private static final String PIZZA_IMAGES_DIR_ABSOLUTE_PATH = PIZZA_IMAGES_DIR.getAbsolutePath() + File.separator;
    
        private static final String FAILED_UPLOAD_MESSAGE = "You failed to upload [%s] because the file because %s";
        private static final String SUCCESS_UPLOAD_MESSAGE = "You successfully uploaded file = [%s]";
    
        @RequestMapping(value = "/uploadFile", method = RequestMethod.POST)
        public ModelAndView uploadFileHandler(@RequestParam("name") String name,
                                          @RequestParam("file") MultipartFile file) {
            ModelAndView modelAndView = new ModelAndView("admin");
    
            if (file.isEmpty()) {
                modelAndView.addObject("message", String.format(FAILED_UPLOAD_MESSAGE, name, "file is empty"));
            } else {
                createPizzaImagesDirIfNeeded();
                modelAndView.addObject("message", createImage(name, file));
            }
    
            return modelAndView;
        }
    
        private void createPizzaImagesDirIfNeeded() {
            if (!PIZZA_IMAGES_DIR.exists()) {
                PIZZA_IMAGES_DIR.mkdirs();
            }
        }
    
        private String createImage(String name, MultipartFile file) {
            try {
                File image = new File(PIZZA_IMAGES_DIR_ABSOLUTE_PATH + name);
                BufferedOutputStream stream = new BufferedOutputStream(new FileOutputStream(image));
                stream.write(file.getBytes());
                stream.close();
    
                return String.format(SUCCESS_UPLOAD_MESSAGE, name);
            } catch (Exception e) {
                return String.format(FAILED_UPLOAD_MESSAGE, name, e.getMessage());
            }
        }
    
        @RequestMapping(value = "/image/{imageName}")
        @ResponseBody
        public byte[] getImage(@PathVariable(value = "imageName") String imageName) throws IOException {
            createPizzaImagesDirIfNeeded();
    
            File serverFile = new File(PIZZA_IMAGES_DIR_ABSOLUTE_PATH + imageName + ".jpg");
    
            return Files.readAllBytes(serverFile.toPath());
        }
    
    }
    
  3. Now let's test our upload functionality. Choose an image and specify a name for it (including extension). Later (after Upload button click) this image will appear in {Tomcat.dir}/pizzaImages folder:

Upload image to server folder

  1. Let's check images show functionality. For this we simply need to include an <img> tag into the placce where we need to show an image (this is how Spring MVC works):

    <img src="/image/11" />

P.S. so you see, that it is simple.

Solution 3

I often run into issues like this depending on my project base. For example, if this is a Maven project in Netbeans, and your uploads go to the target directory, they will be gone the next time you do a clean & build.

If you are sticking them in src/main/webapps/resrouces, they may not be available/removed until you redeploy depending on the settings in your IDE because the application running out of the target directory.

Again this all depends on your setup, IDE, directory structures, etc. I think it has little to do with Spring MVC specifically.

Solution 4

checkout this guide: https://github.com/jdmr/fileUpload it explain how to upload files to "external" folder and then serve the files. look at the controller class here: https://github.com/jdmr/fileUpload/blob/master/src/main/java/org/davidmendoza/fileUpload/web/ImageController.java

Share:
16,798

Related videos on Youtube

Roman
Author by

Roman

Updated on June 26, 2022

Comments

  • Roman
    Roman almost 2 years

    I have Spring MVC app, and I want to place uploaded by user images into resources/uploads folder. Obviously, I want to serve them on my site. But when I tried to place sample.png into resources folder, just for testing purposes, webserver answered "not found". I re-built project and picture became accessible. I deleted picture, it STILL was accessible. I re-built project and server answered what it should ("not found").

    What it this strange behaviour? Are resources being built into final jar file? Does that mean, that all uploaded user pictures will not be accesible before re-building project? If so, I totally shouldn't place uploaded files into resources folder, so where should I place them? Why is that happening, and how should I serve these pictures?

    Thank you very much.

    Context.xml:

    <!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
    
        <!-- Enables the Spring MVC @Controller programming model -->
        <annotation-driven>
            <message-converters>
              <beans:bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                  <beans:property name="objectMapper" ref="customObjectMapper"/>
              </beans:bean>
          </message-converters>
        </annotation-driven>
    
        <!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
        <resources mapping="/resources/**" location="/resources/" />
    
    
        <!-- mustache.java -->
        <beans:bean id="viewResolver" class="org.springframework.web.servlet.view.mustache.MustacheViewResolver">
            <beans:property name="cache" value="false" />
            <beans:property name="prefix" value="/WEB-INF/views/" />
            <beans:property name="suffix" value=".mustache" />
            <beans:property name="templateLoader">
                <beans:bean class="org.springframework.web.servlet.view.mustache.MustacheTemplateLoader" />
            </beans:property>
        </beans:bean>
    
    
        <!-- Standard template engine -->
        <!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
        <!--
        <beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <beans:property name="prefix" value="/WEB-INF/views/" />
            <beans:property name="suffix" value=".jsp" />
        </beans:bean>
        -->
    
        <context:component-scan base-package="com.me.myproject" />
    
    
        <!-- JDBC Data Source. It is assumed you have MySQL running on localhost port 3306 with 
           username root and blank password. Change below if it's not the case -->
          <beans:bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <beans:property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <beans:property name="url" value="jdbc:mysql://localhost:3306/myproject"/>
        <beans:property name="username" value="someone"/>
        <beans:property name="password" value="something"/>
        <beans:property name="validationQuery" value="SELECT 1"/>
      </beans:bean>
    
    
      <!-- FlyWay -->
      <beans:bean id="flyway" class="com.googlecode.flyway.core.Flyway" init-method="migrate">
        <beans:property name="dataSource" ref="myDataSource"/>
      </beans:bean>
    
      <!-- Hibernate Session Factory -->
      <beans:bean id="mySessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean" depends-on="flyway">
        <beans:property name="dataSource" ref="myDataSource"/>
        <beans:property name="packagesToScan">
          <beans:array>
            <beans:value>com.me.myproject</beans:value>
          </beans:array>
        </beans:property>
        <beans:property name="hibernateProperties">
            <beans:value>
               hibernate.dialect=org.hibernate.dialect.MySQLDialect
              hibernate.hbm2ddl.auto=validate
            </beans:value>
          </beans:property>
        </beans:bean>
    
      <!-- Hibernate Transaction Manager -->
      <beans:bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <beans:property name="sessionFactory" ref="mySessionFactory"/>
      </beans:bean>
    
      <!-- Activates annotation based transaction management -->
      <tx:annotation-driven transaction-manager="transactionManager"/>
    
    
    </beans:beans>
    

    Web.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app version="2.5" 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">
    
    
    
    
    
        <!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
        <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/spring/root-context.xml</param-value>
        </context-param>
    
        <!-- Creates the Spring Container shared by all Servlets and Filters -->
        <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>
    
        <!-- Processes application requests -->
        <servlet>
            <servlet-name>appServlet</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
        </servlet>
    
        <servlet-mapping>
            <servlet-name>appServlet</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    
    </web-app>
    
  • Roman
    Roman over 10 years
    Thank you for your answer. But what would be the best practice to serve files from /home/webappFolder/repo? I assume they are not accessible by default?
  • gipinani
    gipinani over 10 years
    They are not accessible by default. You can stream the correct file to the user writing it directly to HttpServletResponse. You can stream only the files that can be accessed by a given user. I also map the folder as a NFS in order to mantain it under our backup storage.

Related