:: Spring form image upload & display from/to Database + Hibernate simple mapping



Setup:
Spring MVC
Hibernate
Maven
Eclipse
m2e (maven) eclipse plugin
jre7


The idea here is to have a web app where we can:
  • upload an image to a database table
  • delete an image from the database table
  • display the images (as well as a few of its properties, ie. name & size) present in a database table


Common issue when using MultipartFile:
If you forget to add enctype="multipart/form-data" to your form element, the uploaded file will simply be ignored...and the url setup to match will not (match).



1. Setup the Database (MySQL in my case)

This DB script is assuming you created a database called ImageManager:

use ImageManager;

CREATE USER 'manager'@'localhost' IDENTIFIED BY 'managerPass';

GRANT ALL ON ImageManager.* TO 'manager'@'localhost';

DROP TABLE IMAGES;

CREATE TABLE IMAGES (
  id             INT PRIMARY KEY AUTO_INCREMENT,
  name           VARCHAR(30),
  contenttype    VARCHAR(30),
  content        MEDIUMBLOB,
  length         INT
);



2. Create the matching DTO class

package image.mymodel;

import ...

@Entity
@Table(name="IMAGES")
public class Image {

  @Id
  @Column(name="ID")
  @GeneratedValue
  private Integer id;
  
  @Column(name="NAME")
  private String name;
  
  @Column(name="CONTENTTYPE")
  private String contentType;
  
  @Column(name="LENGTH")
  private Integer length;
  
  @Column(name="CONTENT")
  @Lob
  private Blob content;
  
  // Getters & Setters
}



3. Setup the controller

3.a Redirect all requests entering our app to the DispatcherServlet
In web.xml add the following
  <servlet>
    <servlet-name>imageapp</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>

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

3.b Configure the spring web configuration for our DispatcherServlet
Create imageapp-servlet.xml (under directory WEB-INF directory) and insert the following
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xmlns:tx="http://www.springframework.org/schema/tx" 
  xmlns:context="http://www.springframework.org/schema/context" 
  xmlns:p="http://www.springframework.org/schema/p" 
  xsi:schemaLocation="http://www.springframework.org/schema/beans 
  http://www.springframework.org/schema/beans/spring-beans-2.5.xsd 
  http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd 
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd" >

  <!-- the application context definition for the springapp DispatcherServlet -->
    <!-- Scans within the base package of the application for @Components to 
    configure as beans -->
  <!-- @Controller, @Service, @Configuration, etc. -->
  <context:annotation-config />
  <context:component-scan base-package="image" />
  
  <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/jsp/" />
    <property name="suffix" value=".jsp" />
  </bean>

  
  <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
    <property name="basename" value="classpath:messages" />
    <property name="defaultEncoding" value="UTF-8" />
  </bean>

  
  <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" p:location="/WEB-INF/jdbc.properties" />

  <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" 
    p:driverClassName="${jdbc.driverClassName}" p:url="${jdbc.databaseurl}" 
    p:username="${jdbc.username}" p:password="${jdbc.password}" />


  <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="configLocation">
      <value>classpath:hibernate.cfg.xml</value>
    </property>
    <property name="configurationClass">
      <value>org.hibernate.cfg.AnnotationConfiguration</value>
    </property>
    <property name="hibernateProperties">
      <props>
        <prop key="hibernate.dialect">${jdbc.dialect}</prop>
        <prop key="hibernate.show_sql">true</prop>
        <prop key="hibernate.connection.SetBigStringTryClob">true</prop>
        <prop key="hibernate.jdbc.batch_size">0</prop>
      </props>
    </property>
  </bean>
  
  <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <!-- one of the properties available; the maximum file size in bytes -->
    <property name="maxUploadSize" value="1000000" />
  </bean>
  
  <tx:annotation-driven />
  
  <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
  </bean>
  
</beans>

3.c Create the Controller
package image.mypackage;


import image.mymodel.Image;
import image.myservice.ImageService;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.Closeable;
import java.io.IOException;
import java.sql.SQLException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
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.multipart.MultipartFile;

@Controller
public class ImageController {

  private final int DEFAULT_BUFFER_SIZE = 10240; // 10KB.
  
  @Autowired
  private ImageService imageService;
  
  @RequestMapping("/admin/manageAllImages")
  public String listImages(Model model){
    model.addAttribute("image", new Image());
    model.addAttribute("imageList", imageService.listImage());
    return "admin/manageallimages";
  }
  
  @RequestMapping(value="/admin/addImage", method=RequestMethod.POST)
  public String addImage(@RequestParam("file") MultipartFile file){
    if(!file.isEmpty())imageService.addImage(file);
    return "redirect:/admin/manageAllImages";
  }
  
  @RequestMapping(value="/admin/deleteImage/{imageId}")
  public String deleteImage(@PathVariable("imageId") Integer id){
      
    imageService.removeImage(id);
    return "redirect:/admin/manageAllImages";
  }
  
  // Display the image...
  @RequestMapping(method=RequestMethod.GET, value="/image/{id}")
  public void getImage(Model model, @PathVariable("id") Integer imageId, HttpServletResponse response) throws ServletException, IOException{
      /* 
       * Big thanks to BalusC for this part
       * cf. his post on http://balusc.blogspot.ch/2007/04/imageservlet.html
       */
      
      // Check if ID is supplied to the request.
      if (imageId == null) {
        // Do your thing if the ID is not supplied to the request.
        // Throw an exception, or send 404, or show default/warning image, or just ignore it.
        response.sendError(HttpServletResponse.SC_NOT_FOUND); // 404.
        return;
      }
        
      // Lookup Image by ImageId in database.
      // Do your "SELECT * FROM Image WHERE ImageID" thing.
      Image image = imageService.getImage(imageId);

      // Check if image is actually retrieved from database.
      if (image == null) {
        // Do your thing if the image does not exist in database.
        // Throw an exception, or send 404, or show default/warning image, or just ignore it.
        response.sendError(HttpServletResponse.SC_NOT_FOUND); // 404.
        return;
      }

      // Init servlet response.
      response.reset();
      response.setBufferSize(DEFAULT_BUFFER_SIZE);
      response.setContentType(image.getContentType());
      response.setHeader("Content-Length", String.valueOf(image.getLength()));
      response.setHeader("Content-Disposition", "inline; filename=\"" + image.getName() + "\"");

      // Prepare streams.
      BufferedInputStream input = null;
      BufferedOutputStream output = null;

      try {
        // Open streams.
        try {
          input = new BufferedInputStream(image.getContent().getBinaryStream(), DEFAULT_BUFFER_SIZE);
        } catch (SQLException e) {
          e.printStackTrace();
        }
        output = new BufferedOutputStream(response.getOutputStream(), DEFAULT_BUFFER_SIZE);

        // Write file contents to response.
        byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
        int length;
        while ((length = input.read(buffer)) > 0) {
          output.write(buffer, 0, length);
        }
      } finally {
        // Gently close streams.
        close(output);
        close(input);
      }
    }
    
    // Helper (can be refactored to public utility class)
    private static void close(Closeable resource) {
      if (resource != null) {
        try {
          resource.close();
        } catch (IOException e) {
          // Do your thing with the exception. Print it, log it or mail it.
          e.printStackTrace();
        }
      }
    }
    
}


4. Create the Service Class

You have to create the ImageService interface as well... I'll leave that to you as it is extremely simple.

package image.myservice;

import image.mydao.ImageDAO;
import image.mymodel.Image;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

@Service
public class ImageServiceImpl implements ImageService{

  @Autowired
  ImageDAO imageDAO;
  
  @Transactional
  public void addImage(MultipartFile file) {
    imageDAO.addImage(file);
  }

  @Transactional
  public List<Image> listImage() {
    return imageDAO.listImage();
  }

  @Transactional
  public void removeImage(Integer id) {
    imageDAO.removeImage(id);
  }

  @Transactional
  public Image getImage(Integer id) {
    return imageDAO.getImage(id);
  }

  @Transactional
  public void updateImage(Image account) {
    imageDAO.updateImage(account);
  }

}



5. Create the DAO class

You have to create the ImageDAO interface as well... I'll leave that to you as it is extremely simple.

package image.mydao;

import image.mymodel.Image;

import java.io.IOException;
import java.sql.Blob;
import java.util.List;

import org.hibernate.Hibernate;
import org.hibernate.SessionFactory;
import org.hibernate.engine.jdbc.LobCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.web.multipart.MultipartFile;

@Repository
public class ImageDAOHibernate implements ImageDAO {

  @Autowired
  private SessionFactory sessionFactory;

  public void addImage(MultipartFile file) {
      Image image = new Image();
      Blob blob = null;
      
      image.setName(file.getOriginalFilename());
      image.setContentType(file.getContentType());
      image.setLength((int) file.getSize());
      
      try {
        LobCreator lobCreator = Hibernate.getLobCreator(sessionFactory.getCurrentSession());
      blob = lobCreator.createBlob(file.getInputStream(),file.getSize());
    } catch (IOException e) {
      e.printStackTrace();
    }
      
      image.setContent(blob);
      
    sessionFactory.getCurrentSession().save(image);
  }

  public List<Image> listImage() {
    return sessionFactory.getCurrentSession().createQuery("from Image").list();
  }

  public void removeImage(Integer id) {
    Image image = this.getImage(id);
        if (null != image) {
            sessionFactory.getCurrentSession().delete(image);
        }
  }

  public Image getImage(Integer id) {
    return (Image) sessionFactory.getCurrentSession().get(Image.class, id);
  }

  public void updateImage(Image image) {
    sessionFactory.getCurrentSession().saveOrUpdate(image);
  }

}



6. The hibernate config file

Create hibernate.cfg.xml (under src/main/resources/ directory) and insert the following

<?xml version='1.0' encoding='utf-8'?>
  <!DOCTYPE hibernate-configuration PUBLIC
    "-//Hibernate/Hibernate Configuration DTD//EN"
    "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
   
  <hibernate-configuration>
    <session-factory>
      <mapping class="image.mymodel.Image" />
    </session-factory>
  </hibernate-configuration>



7. The DB connection config
Create jdbc.properties (under WEB-INF directory) and insert the following
  jdbc.driverClassName= com.mysql.jdbc.Driver
  jdbc.dialect=org.hibernate.dialect.MySQLDialect
  jdbc.databaseurl=jdbc:mysql://localhost:3306/ImageManager
  jdbc.username=manager
  jdbc.password=managerPass


8. Create index.jsp
...under webapp directory and insert the following
<a href="${pageContext.request.contextPath}/admin/manageAllImages">${pageContext.request.contextPath}/admin/manageAllImages</a>


9. Create manageallimages.jsp
...under webapp/WEB-INF/jsp/admin/ and insert the following
<%@taglib uri="http://www.springframework.org/tags" prefix="spring"%>
<%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<html>
<head>
  <title>Image Manager</title>
</head>
<body>

<h2>Image Manager</h2>

<form:form method="post" action="addImage" commandName="image" enctype="multipart/form-data">
  <table>
    <tr>
      <td><form:label path="content">Image</form:label></td>
      <td><input type="file" name="file" id="file"></input></td>
    </tr>
    <tr>
      <td colspan="2">
        <input type="submit" value="Upload"/>
      </td>
    </tr>
  </table>
</form:form>
<br>
<br>
<h3>All the images</h3>
<c:if test="${!empty imageList}">
  <table>
    <tr>
      <th>Name | </th>
      <th>Content Type | </th>
      <th>Size | </th>
      <!-- <th>Pic | </th> -->
      <th>&nbsp;</th>
    </tr>
    <c:forEach items="${imageList}" var="image">
        <tr>
        <td>${image.name},</td>
        <td>${image.contentType}</td>
        <td>${image.length} bytes</td>
        <td><img src="${pageContext.request.contextPath}/image/${image.id}" /> </td>
        <td><a href="deleteImage/${image.id}">Delete Image</a></td>
      </tr>
    </c:forEach>
  </table>
</c:if>
 
</body>
</html>


10. Oh, and my pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
 
  <groupId>springhibernateimgmanager</groupId>
  <artifactId>springhibernateimgmanager</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>
   
  <name>Spring + Hibernate image management project</name>
  <properties>
    <org.springframework.version>3.0.5.RELEASE</org.springframework.version>
    <javax.servlet.jstl.version>1.2</javax.servlet.jstl.version>
  </properties>
  
  <dependencies>
    <!-- Hibernate deps -->
    <dependency>
      <groupId>org.hibernate.javax.persistence</groupId>
      <artifactId>hibernate-jpa-2.0-api</artifactId>
      <version>1.0.1.Final</version>
    </dependency>
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-entitymanager</artifactId>
      <version>3.6.10.Final</version>
    </dependency>
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-core</artifactId>
      <version>3.6.10.Final</version>
    </dependency>
    
    <!-- Spring deps -->
     <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${org.springframework.version}</version>
     </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${org.springframework.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>${org.springframework.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>${org.springframework.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-orm</artifactId>
      <version>${org.springframework.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>${org.springframework.version}</version>
      <scope>test</scope>
    </dependency>

    <!-- Misc deps -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jstl</artifactId>
      <version>${javax.servlet.jstl.version}</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.10</version>
    </dependency>
    <dependency>
      <groupId>commons-dbcp</groupId>
      <artifactId>commons-dbcp</artifactId>
      <version>1.4</version>
    </dependency>
    <dependency>
      <groupId>commons-pool</groupId>
      <artifactId>commons-pool</artifactId>
      <version>20030825.183949</version>
    </dependency>
    <dependency>
      <groupId>commons-fileupload</groupId>
      <artifactId>commons-fileupload</artifactId>
      <version>1.2.2</version> <!-- makesure correct version here -->
    </dependency>
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-io</artifactId>
      <version>1.3.2</version>
    </dependency>
     
  </dependencies>
 
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>1.6</source>
          <target>1.6</target>
          <encoding>UTF8</encoding>
        </configuration>
        <inherited>true</inherited>
      </plugin>
    </plugins>
  
    <resources>
      <resource>
     <directory>src/main/resources</directory>
     <filtering>true</filtering>
      </resource>
    </resources>
    <testResources>
      <testResource>
        <directory>src/test/resources</directory>
        <filtering>true</filtering>
      </testResource>
      <testResource>
        <directory>src/main/resources</directory>
        <filtering>true</filtering>
      </testResource>
    </testResources>
  </build>
</project>


You can download a working version of this project at the bottom of this page.


Resources



Č
ċ
Adrien Bebe,
Oct 7, 2012, 4:45 AM
Comments