SpringBoot RestAPIs + Thymeleaf – Upload/Download Multiple Files to FileSystem Example

springboot-restapi-upload-download-multiple-files-feature-image

In the tutorial, we show you how to create a SpringBoot RestAPIs to upload multiple files to FileSystem with Thymeleaf frontend.

Technologies

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

Goal

– We create a SpringBoot project as below:

springboot-restapi-upload-download-multiple-files-project-structure

– Upload Multiple Files ->

springboot-restapi-upload-download-multiple-files-thymeleaf-upload-form

springboot-restapi-upload-download-multiple-files-thymeleaf-upload-results

– Download forms ->

springboot-restapi-upload-download-multiple-files-thymeleaf-list-all-uploaded-files

springboot-restapi-upload-download-multiple-files-thymeleaf-downloaded-files

How to upload multiples files?

– We use html multiple attribute to build upload form:

– In backend controller, we handle the multiple uploaded files as below:

@PostMapping("/")
public String uploadMultipartFile(@RequestParam("files") MultipartFile[] files, Model model) {
	List fileNames = null;
	
	try {
		fileNames = Arrays.asList(files)
				.stream()
				.map(file -> {
					fileStorage.store(file);
					return file.getOriginalFilename();
				})
				.collect(Collectors.toList());
		
		model.addAttribute("message", "Files uploaded successfully!");
		model.addAttribute("files", fileNames);
	} catch (Exception e) {
		model.addAttribute("message", "Fail!");
		model.addAttribute("files", fileNames);
	}
	
	return "uploadform";
}

Practice

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

Implement File Storage Service

– Create FileStorage.java interface:

package com.grokonez.uploadmultifiles.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 file storage with FileStorageImpl.java:

package com.grokonez.uploadmultifiles.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.nio.file.StandardCopyOption;
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()), StandardCopyOption.REPLACE_EXISTING);
        } 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");
        }
	}
}
Create FileInfo Model

FileInfo.java ->

package com.grokonez.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;
	}
}

Upload Multiple Files Controller

UploadFileController.java ->

package com.grokonez.uploadmultifiles.controller;

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

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.grokonez.uploadmultifiles.filestorage.FileStorage;

@Controller
public class UploadFileController {
	
	@Autowired
	FileStorage fileStorage;
	
    @GetMapping("/")
    public String index() {
        return "uploadform";
    }
    
    @PostMapping("/")
    public String uploadMultipartFile(@RequestParam("files") MultipartFile[] files, Model model) {
    	List fileNames = null;
    	
		try {
	        fileNames = Arrays.asList(files)
	                .stream()
	                .map(file -> {
	                	fileStorage.store(file);
	                	return file.getOriginalFilename();
	                })
	                .collect(Collectors.toList());
			
			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

DownloadFileController.java ->

package com.grokonez.uploadmultifiles.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.grokonez.uploadmultifiles.filestorage.FileStorage;
import com.grokonez.uploadmultifiles.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 "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);	
	}
}
Init FileStorage with Main class

SpringBootUploadMultiFileToFileSystemApplication.java ->

package com.grokonez.uploadmultifiles;

import javax.annotation.Resource;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import com.grokonez.uploadmultifiles.filestorage.FileStorage;

@SpringBootApplication
public class SpringBootUploadMultiFileToFileSystemApplication implements CommandLineRunner {

	@Resource
	FileStorage fileStorage;
	
	public static void main(String[] args) {
		SpringApplication.run(SpringBootUploadMultiFileToFileSystemApplication.class, args);
	}
	
	@Override
	public void run(String... args) throws Exception {
		fileStorage.deleteAll();
		fileStorage.init();
	}
}
FrontEnd Implementation
Thymeleaf Upload Form

uploadform.html ->




    Upload Multiple Files
    
    
	%MINIFYHTMLb0f6972ca6374eb8f2138441606fbbe616%%MINIFYHTMLb0f6972ca6374eb8f2138441606fbbe617%%MINIFYHTMLb0f6972ca6374eb8f2138441606fbbe618%
 

	

Upload Multiple Files to FileSystem

Files

%MINIFYHTMLb0f6972ca6374eb8f2138441606fbbe619%
Thymeleaf Download Form

listfiles.html ->




    Upload Multiple Files
    
    
	%MINIFYHTMLb0f6972ca6374eb8f2138441606fbbe620%%MINIFYHTMLb0f6972ca6374eb8f2138441606fbbe621%%MINIFYHTMLb0f6972ca6374eb8f2138441606fbbe622%
 

	

Upload Multiple Files to FileSystem

Files

SourceCode

SpringBootUploadMultipleFileToFileSystem



By grokonez | November 5, 2018.


Related Posts


Got Something To Say:

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

*