SpringBoot Upload/Download Files Example – MultipartFile + Thymeleaf + Bootstrap 4

springboot-download-upload-files-multipartFile-thymeleaf-bootstrap 4

In the tutorial, we guide how to build a SpringBoot web-application to upload/download file with Thymeleaf engine and Bootstrap 4.

Technologies

  • Java 8
  • Maven 3.6.1
  • Spring Tool Suite: Version 3.9.4.RELEASE
  • Spring Boot: 2.0.2.RELEASE
  • Bootstrap 4

Goal

We create a SpringBoot project as below:

SpringBoot-Upload-Download-MultipartFile-Thymeleaf-Bootstrap4-project-structure

-> Upload Form:

SpringBoot-Upload-Download-MultipartFile-Thymeleaf-Bootstrap4-upload-view

-> Download Form:

Practice

We create a SpringBoot project with below dependencies:


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


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

Frontend
Upload Multipart-Form

– Create /templates/multipartfile/uploadfile.html file:




    Upload MultipartFile
    
    
	
	
	
	

 

	

Upload MultipartFile to FileSystem

Files
Retrieves Files

– Create /templates/multipartfile/listfiles.html file:




    Upload MultipartFile
    
    
	
	
	
	



	

Uploaded Files

No Filename Download
1 File-Name Link
Backend
File Storage Service

– Create interface FileStorage.java file:

package com.javasampleapproach.multipartfile.filestorage;

import java.nio.file.Path;
import java.util.stream.Stream;

import org.springframework.core.io.Resource;
import org.springframework.web.multipart.MultipartFile;

public interface FileStorage {
	public void store(MultipartFile file);
	public Resource loadFile(String filename);
	public void deleteAll();
	public void init();
	public Stream loadFiles();
}

– Implement above interface by class FileStorageImpl.java:

package com.javasampleapproach.multipartfile.filestorage;

import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.stream.Stream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.stereotype.Service;
import org.springframework.util.FileSystemUtils;
import org.springframework.web.multipart.MultipartFile;

@Service
public class FileStorageImpl implements FileStorage{
	
	Logger log = LoggerFactory.getLogger(this.getClass().getName());
	private final Path rootLocation = Paths.get("filestorage");
 
	@Override
	public void store(MultipartFile file){
		try {
            Files.copy(file.getInputStream(), this.rootLocation.resolve(file.getOriginalFilename()));
        } catch (Exception e) {
        	throw new RuntimeException("FAIL! -> message = " + e.getMessage());
        }
	}
	
	@Override
    public Resource loadFile(String filename) {
        try {
            Path file = rootLocation.resolve(filename);
            Resource resource = new UrlResource(file.toUri());
            if(resource.exists() || resource.isReadable()) {
                return resource;
            }else{
            	throw new RuntimeException("FAIL!");
            }
        } catch (MalformedURLException e) {
        	throw new RuntimeException("Error! -> message = " + e.getMessage());
        }
    }
    
	@Override
    public void deleteAll() {
        FileSystemUtils.deleteRecursively(rootLocation.toFile());
    }

	@Override
    public void init() {
        try {
            Files.createDirectory(rootLocation);
        } catch (IOException e) {
            throw new RuntimeException("Could not initialize storage!");
        }
    }

	@Override
	public Stream loadFiles() {
        try {
            return Files.walk(this.rootLocation, 1)
                .filter(path -> !path.equals(this.rootLocation))
                .map(this.rootLocation::relativize);
        }
        catch (IOException e) {
        	throw new RuntimeException("\"Failed to read stored file");
        }
	}
}
Upload/Download File Controller

– Firstly, create a simple data model FileInfo.java that contains information of a file:

package com.javasampleapproach.multipartfile.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 controller UploadFileController.java:

package com.javasampleapproach.multipartfile.controller;

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.javasampleapproach.multipartfile.filestorage.FileStorage;

@Controller
public class UploadFileController {
	
	@Autowired
	FileStorage fileStorage;
	
    @GetMapping("/")
    public String index() {
        return "multipartfile/uploadform.html";
    }
    
    @PostMapping("/")
    public String uploadMultipartFile(@RequestParam("uploadfile") MultipartFile file, Model model) {
		try {
			fileStorage.store(file);
			model.addAttribute("message", "File uploaded successfully! -> filename = " + file.getOriginalFilename());
		} catch (Exception e) {
			model.addAttribute("message", "Fail! -> uploaded filename: " + file.getOriginalFilename());
		}
        return "multipartfile/uploadform.html";
    }
    
    
}

– Implement controller DownloadFileController.java:

package com.javasampleapproach.multipartfile.controller;

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

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
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.javasampleapproach.multipartfile.filestorage.FileStorage;
import com.javasampleapproach.multipartfile.model.FileInfo;

@Controller
public class DownloadFileController {

	@Autowired
	FileStorage fileStorage;
	
	/*
	 * Retrieve Files' Information
	 */
	@GetMapping("/files")
	public String getListFiles(Model model) {
		List fileInfos = fileStorage.loadFiles().map(
					path ->	{
						String filename = path.getFileName().toString();
						String url = MvcUriComponentsBuilder.fromMethodName(DownloadFileController.class,
		                        "downloadFile", path.getFileName().toString()).build().toString();
						return new FileInfo(filename, url); 
					} 
				)
				.collect(Collectors.toList());
		
		model.addAttribute("files", fileInfos);
		return "multipartfile/listfiles";
	}
 
    /*
     * Download Files
     */
	@GetMapping("/files/{filename}")
	public ResponseEntity downloadFile(@PathVariable String filename) {
		Resource file = fileStorage.loadFile(filename);
		return ResponseEntity.ok()
					.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + file.getFilename() + "\"")
					.body(file);	
	}
}

Note:
We can configure multipart.max-file in application.properties to limit the size of uploaded file:

spring.http.multipart.max-file-size=500KB
spring.http.multipart.max-request-size=500KB
Run & Check Results

Run the SpringBoot project, then makes upload/download requests ->

Upload File

SpringBoot-Upload-Download-MultipartFile-Thymeleaf-Bootstrap4-upload-file-view

-> Browser Network logs:

SpringBoot-Upload-Download-MultipartFile-Thymeleaf-Bootstrap4-upload-file-network-logs

-> Stored files:

SpringBoot-Upload-Download-MultipartFile-Thymeleaf-Bootstrap4-all-uploaded-files

Retrieve Files

SpringBoot-Upload-Download-MultipartFile-Thymeleaf-Bootstrap4-retrieve-files

Download Files

SpringBoot-Upload-Download-MultipartFile-Thymeleaf-Bootstrap4-download-files-view

-> Browser Network logs:

SpringBoot-Upload-Download-MultipartFile-Thymeleaf-Bootstrap4-download-files

SourceCode

SpringUploadDownloadMultipartFile

17 thoughts on “SpringBoot Upload/Download Files Example – MultipartFile + Thymeleaf + Bootstrap 4”

  1. Showing error in :fileName variable below used (error: fileName cant resolved to a variable)
    public String getListFiles(Model model) {
    model.addAttribute(“files”,
    files.stream()
    .map(fileName -> MvcUriComponentsBuilder
    .fromMethodName(UploadController.class, “getFile”, fileName).build().toString())
    .collect(Collectors.toList()));
    model.addAttribute(“totalFiles”, “TotalFiles: ” + files.size());
    return “listFiles”;
    }

  2. If it is possible please try to provide an example with the using of streams and tests and restful without JSP and that stores to mysql or mongodb.
    Thanks!

    1. We use a requestMapping in UploadController to handle upload file:

      @Autowired
      StorageService storageService;
      
      ...
      
      @PostMapping("/")
      public String handleFileUpload(@RequestParam("file") MultipartFile file, Model model) {
          try {
      	storageService.store(file);
          ...
      

      Then use storageService to store MultipartFile file.

  3. Document upload size is woring as excepcted in local. howver in jboss server it got failed with the error message

    {"timestamp":1511159296573,"status":500,"error":"Internal Server Error","exception":"java.lang.RuntimeException","message":"java.io.IOException: UT000020: Connection terminated as request was larger than 10485760",
    

    is there any configuration for below 2 property in jboss undertow config file
    spring.http.multipart.max-file-size=500KB
    spring.http.multipart.max-request-size=500KB

    1. Hello Prathwish Hegde,

      Spring Boot includes support for embedded Tomcat, Jetty, and Undertow servers.
      So I think you should start with embedded Undertow as configuration in pom.xml file:

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

      Regards,
      JSA

  4. thx!!!
    i have a problem: the spring boot app runs perfect.. the files copied to “upload-dir”
    but… when i change the app to .war and run in tomcat NOT FOUND x(

    …. tryed to copy the entiry directory manually but still fails… x(

    HELP pls!

    1. Hi Adolf Mrls,

      Do you have check the permission for create-read on your folder?
      Can you try to work with absolute path of ‘upload-dir’.

      Regards,
      JSA

  5. hi, i tried your code but when i want to test it with postman it replies with
    {
    “timestamp”: “2018-04-09T13:20:24.966+0000”,
    “status”: 400,
    “error”: “Bad Request”,
    “message”: “Required request part ‘file’ is not present”,
    “path”: “/”
    }
    please any help ?

  6. Hi there,

    Would you be open to discussing some observations I’ve made on ozenero.com? I think we can help to drive more traffic to yoursite, in addition to more sales.

    If more sales/leads is what you’d like, just respond here, and I’ll have one of my consultants get in touch.

    Thanks,

    Liv

Leave a Reply

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