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

52 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

  7. Nice post. I used to be checking continuously this blog and I am inspired!
    Very helpful information specially the last section ūüôā I deal with such info much.
    I was looking for this certain info for a long time.
    Thanks and best of luck.

  8. Good post. I learn something totally new and challenging on websites I stumbleupon everyday.
    It’s always useful to read through articles from
    other writers and use something from their web sites.

  9. I’ve been browsing online more than 4 hours today, yet I never found any interesting article like yours.
    It’s pretty worth enough for me. In my view, if all web owners and bloggers made
    good content as you did, the net will be much more
    useful than ever before.

  10. Hmm is anyone else having problems with the images on this blog loading?
    I’m trying to determine if its a problem on my end or if it’s the blog.
    Any suggestions would be greatly appreciated.

  11. Hey! This post couldn’t be written any better! Reading this post
    reminds me of my old room mate! He always kept chatting about this.
    I will forward this post to him. Pretty sure he will have a good read.
    Thank you for sharing!

  12. A fascinating discussion is definitely worth comment.
    I believe that you ought to write more about this issue, it may not be a taboo subject
    but usually people do not speak about these subjects.
    To the next! Kind regards!!

  13. I’m really enjoying the design and layout of your site.
    It’s a very easy on the eyes which makes it much more
    pleasant for me to come here and visit more often. Did you hire out a developer to create your
    theme? Excellent work!

  14. Thank you for another informative blog. The place else may just I get that kind of info written in such an ideal approach?

    I have a challenge that I am just now operating on, and I have been on the glance out for such information.

  15. Somebody essentially help to make significantly posts I would state.
    That is the first time I frequented your website page and thus
    far? I amazed with the research you made to create this particular
    publish extraordinary. Fantastic job!

  16. Have you ever thought about adding a little bit more than just your articles?
    I mean, what you say is fundamental and all.

    Nevertheless think of if you added some great images or videos to give your
    posts more, “pop”! Your content is excellent but with pics and clips, this site could
    certainly be one of the most beneficial in its niche. Amazing blog!

  17. Its like you read my mind! You appear to know so much about this,
    like you wrote the book in it or something. I think that you can do with some pics to drive the message home a bit, but other than that, this is
    excellent blog. An excellent read. I’ll definitely be back.

  18. My brother recommended I might like this blog.
    He was entirely right. This post actually made my day. You can not
    imagine simply how much time I had spent for this info!

    Thanks!

  19. My spouse and I stumbled over here coming from a different
    web page and thought I might as well check things out.
    I like what I see so i am just following you. Look forward to exploring your web page yet again.

Leave a Reply

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