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>
Here is a ready-for-use images upload / download controller exactly for this purpose:
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>
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());
}
}
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:
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With