SpringBoot Upload Multiple Files to MySQL Example – Thymeleaf + Spring JPA Audit

springboot-restapi-uplacoad-multiple-files-to-mysql-spring-jpa-audit-date-feature-image

In the tutorial, we show you how to build a SpringBoot RestAPIs to upload multiple files to MySQL database with Thymeleaf frontend, and Spring JPA Audit feature.

Related posts:
Spring Data JPA Auditing Example – SpringBoot RestAPIs + MySQL
SpringBoot RestAPIs + Thymeleaf – Upload/Download Multiple Files to FileSystem Example

Technologies

– Java 8
– Maven 3.6.1
– SpringBoot
– Thymeleaf
– Bootstrap 4

Goal

– We create a SpringBoot project as below:

springboot-restapi-upload-multiple-files-to-mysql-spring-jpa-audit-date-project-structures

– Upload Multiple Files ->

springboot-restapi-upload-multiple-files-to-mysql-spring-jpa-audit-date-upload-successfully

springboot-restapi-upload-multiple-files-to-mysql-spring-jpa-audit-date-updated-records

– Download Form ->

springboot-restapi-upload-multiple-files-to-mysql-spring-jpa-audit-date-download-files

Practice

SpringBoot Backend Implementation
Create SpringBoot project

We use SpringToolSuite to create a SpringBoot project with below dependencies:


	org.springframework.boot
	spring-boot-starter-thymeleaf


	org.springframework.boot
	spring-boot-starter-web


	org.springframework.boot
	spring-boot-starter-data-jpa


	mysql
	mysql-connector-java
	runtime



    com.fasterxml.jackson.datatype
    jackson-datatype-jsr310

jackson-datatype-jsr310 is used to support Java 8 Date/Time for JPA Audit feature.

Implement DateAudit

– Configure JPA Auditing:

package com.ozenero.uploadmultifiles.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
 
@Configuration
@EnableJpaAuditing
public class JpaAuditingConfig {}

– Implement DateAudit ->

package com.ozenero.uploadmultifiles.model.audit;

import java.time.Instant;

import javax.persistence.Column;
import javax.persistence.EntityListeners;
import javax.persistence.MappedSuperclass;
 
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
 
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
@JsonIgnoreProperties(
        value = {"createdAt", "updatedAt"},
        allowGetters = true
)
public abstract class DateAudit {
 
    @CreatedDate
    @Column(nullable = false, updatable = false)
    private Instant createdAt;
 
    @LastModifiedDate
    @Column(nullable = false)
    private Instant updatedAt;
 
    public Instant getCreatedAt() {
        return createdAt;
    }
 
    public void setCreatedAt(Instant createdAt) {
        this.createdAt = createdAt;
    }
 
    public Instant getUpdatedAt() {
        return updatedAt;
    }
 
    public void setUpdatedAt(Instant updatedAt) {
        this.updatedAt = updatedAt;
    }
}

@EntityListeners annotation is used to specify callback listener classes. We use the JPA entity listener class of Spring Data AuditingEntityListener.
@MappedSuperClass annotation is used to move the properties to a base class DateAudit which would be extended by all your audited entities.

Build File Model

FileModel.java ->

package com.ozenero.uploadmultifiles.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Lob;
import javax.persistence.Table;

import com.ozenero.uploadmultifiles.model.audit.DateAudit;
 
@Entity
@Table(name="file_model")
public class FileModel  extends DateAudit {
	@Id
	@GeneratedValue
    @Column(name = "id")
    private Long id;
	
    @Column(name = "name")
	private String name;
    
    @Column(name = "mimetype")
	private String mimetype;
	
	@Lob
    @Column(name="pic")
    private byte[] pic;
	
	public FileModel(){}
	
	public FileModel(String name, String mimetype, byte[] pic){
		this.name = name;
		this.mimetype = mimetype;
		this.pic = pic;
	}
	
	public Long getId(){
		return this.id;
	}
	
	public void setId(Long id){
		this.id = id;
	}
	
	public String getName(){
		return this.name;
	}
	
	public void setName(String name){
		this.name = name;
	}
	
	public String getMimetype(){
		return this.mimetype;
	}
	
	public void setMimetype(String mimetype){
		this.mimetype = mimetype;
	}
	
	public byte[] getPic(){
		return this.pic;
	}
	
	public void setPic(byte[] pic){
		this.pic = pic;
	}
}
Define File Repository

FileRepository.java ->

package com.ozenero.uploadmultifiles.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.transaction.annotation.Transactional;

import com.ozenero.uploadmultifiles.model.FileModel;

@Transactional
public interface FileRepository extends JpaRepository{	
	public FileModel findByName(String name);
}
Upload Multiple Files Controller

UploadFileController.java ->

package com.ozenero.uploadmultifiles.controller;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;

import com.ozenero.uploadmultifiles.model.FileModel;
import com.ozenero.uploadmultifiles.repository.FileRepository;

@Controller
public class UploadFileController {
	
	@Autowired
	FileRepository fileRepository;
	
    @GetMapping("/")
    public String index() {
        return "uploadform";
    }
    
    @PostMapping("/")
    public String uploadMultipartFile(@RequestParam("files") MultipartFile[] files, Model model) {
    	List fileNames = new ArrayList();
    	
		try {
			List storedFile = new ArrayList();
			
			for(MultipartFile file: files) {
				FileModel fileModel = fileRepository.findByName(file.getOriginalFilename());
				if(fileModel != null) {
					// update new contents
					fileModel.setPic(file.getBytes());
				}else {
					fileModel = new FileModel(file.getOriginalFilename(), file.getContentType(), file.getBytes());
				}
				
				fileNames.add(file.getOriginalFilename());				
				storedFile.add(fileModel);
			}
			
			// Save all Files to database
	    	fileRepository.saveAll(storedFile);
	    	
			model.addAttribute("message", "Files uploaded successfully!");
			model.addAttribute("files", fileNames);
		} catch (Exception e) {
			model.addAttribute("message", "Fail!");
			model.addAttribute("files", fileNames);
		}
		
        return "uploadform";
    }
}

– We can limit the size of uploaded file by using below configuration in application.properties ->

spring.servlet.multipart.max-file-size=500KB
spring.servlet.multipart.max-request-size=500KB
Download Files Controller

– Firstly, create FileInfo.java ->

package com.ozenero.uploadmultifiles.model;

public class FileInfo {
	private String filename;
	private String url;
	
	public FileInfo(String filename, String url) {
		this.filename = filename;
		this.url = url;
	}
	
	public String getFilename() {
		return this.filename;
	}
	
	public void setFilename(String filename) {
		this.filename = filename;
	}
	
	public String getUrl() {
		return this.url;
	}
	
	public void setUrl(String url) {
		this.url = url;
	}
}

– Implement download controller DownloadFileController.java ->

package com.ozenero.uploadmultifiles.controller;

import java.util.List;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;

import com.ozenero.uploadmultifiles.model.FileInfo;
import com.ozenero.uploadmultifiles.model.FileModel;
import com.ozenero.uploadmultifiles.repository.FileRepository;

@Controller
public class DownloadFileController {

	@Autowired
	FileRepository fileRepository;
	
	/*
	 * Retrieve Files' Information
	 */
	@GetMapping("/files")
	public String getListFiles(Model model) {
		List fileInfos = fileRepository.findAll().stream().map(
				fileModel ->	{
					String filename = fileModel.getName();
					String url = MvcUriComponentsBuilder.fromMethodName(DownloadFileController.class,
	                        "downloadFile", fileModel.getName().toString()).build().toString();
					return new FileInfo(filename, url); 
				} 
			)
			.collect(Collectors.toList());
	
		model.addAttribute("files", fileInfos);
		return "listfiles";
	}
 
    /*
     * Download Files
     */
	@GetMapping("/files/{filename}")
	public ResponseEntity downloadFile(@PathVariable String filename) {
		FileModel file = fileRepository.findByName(filename);
		return ResponseEntity.ok()
					.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + file.getName() + "\"")
					.body(file.getPic());	
	}
}
Application Configuration

application.properties ->

spring.datasource.url=jdbc:mysql://localhost:3306/testdb?useSSL=false&serverTimezone=UTC&useLegacyDatetimeCode=false
spring.datasource.username=root
spring.datasource.password=12345
spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=create-drop

spring.servlet.multipart.max-file-size=500KB
spring.servlet.multipart.max-request-size=500KB

## Hibernate Logging
logging.level.org.hibernate.SQL= DEBUG
 
## Jackson Properties
spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS= false
spring.jackson.time-zone= UTC
Main Class Implementation

– Set the default timezone for our application to UTC.

package com.ozenero.uploadmultifiles;

import java.util.TimeZone;

import javax.annotation.PostConstruct;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.data.jpa.convert.threeten.Jsr310JpaConverters;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@SpringBootApplication
@EntityScan(basePackageClasses = { 
		SpringBootUploadMultiFileToMySqlApplication.class,
		Jsr310JpaConverters.class 
})
@EnableTransactionManagement
public class SpringBootUploadMultiFileToMySqlApplication {

	@PostConstruct
	void init() {
		TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
	}

	
	public static void main(String[] args) {
		SpringApplication.run(SpringBootUploadMultiFileToMySqlApplication.class, args);
	}
}
Thymeleaf Frontend Implementation
Upload Form

uploadform.html ->




    Upload Multiple Files
    
    
	
	
	
	

 

	

Upload Multiple Files to MySQL

Files

Download Form

listfiles.html ->




    Upload Multiple Files to MySQL
    
    
	
	
	
	



	

Uploaded Files

No Filename Download
1 File-Name Link
Run & Check Results

Run the SpringBoot project,

– Upload Multiples files:

springboot-restapi-upload-multiple-files-to-mysql-spring-jpa-audit-date-uploaded-files

springboot-restapi-upload-multiple-files-to-mysql-spring-jpa-audit-date-form-upload-files

-> Results:

springboot-restapi-upload-multiple-files-to-mysql-spring-jpa-audit-date-upload-successfully

springboot-restapi-upload-multiple-files-to-mysql-spring-jpa-audit-date-records-in-file_models_tables

– Change text in ozenero-about-us.txt then upload 3 files again, results ->

springboot-restapi-upload-multiple-files-to-mysql-spring-jpa-audit-date-updated-records

– Download files:

springboot-restapi-upload-multiple-files-to-mysql-spring-jpa-audit-date-download-files

-> results:

springboot-restapi-upload-multiple-files-to-mysql-spring-jpa-audit-date-check-downloaded-files-images

SourceCode

SpringBootUploadMultiFileToMySQL

One thought on “SpringBoot Upload Multiple Files to MySQL Example – Thymeleaf + Spring JPA Audit”

  1. hi,

    thanks for sharing the process on upload and download file to mysql.
    this works like a charm when i run it as a stand alone application, but when i apply the same within my project i get a sql error stating “Unknown column ‘filemodel0_.createdAt’ in ‘field list'”

    request you to kindly help me on the same and have been trying to figure this out where am going wrong for days now

Leave a Reply

Your email address will not be published. Required fields are marked *