Spring Boot File upload example with Multipart File

In this tutorial, I will show you how to upload and download files with a Spring Boot Rest APIs to/from a static folder. We also use Spring Web MultipartFile interface to handle HTTP multi-part requests.

This Spring Boot App works with:
Angular 8 / Angular 10 / Angular 11 / Angular 12 / Angular 13 / Angular 14 / Angular 15
Angular Material 12
Vue Client / Vuetify Client
React Client / React Hooks Client
React Image Upload with Preview
Material UI Client
Axios Client

Related Posts:
Spring WebFlux File upload example
Spring Boot Delete File example
Spring Boot Thymeleaf File Upload example
How to upload multiple files in Java Spring Boot
Spring Boot Upload/Download File to/from Database example
Spring Boot: Upload/Import Excel file data into MySQL Database
Spring Boot: Upload/Import CSV file data into MySQL Database
– Documentation: Spring Boot Swagger 3 example
– Caching: Spring Boot Redis Cache example
– Validation: Spring Boot Validate Request Body

Deployment: Deploy Spring Boot App on AWS – Elastic Beanstalk


Spring Boot Rest APIs for uploading Files

Our Spring Boot Application will provide APIs for:

  • uploading File to a static folder in the Server
  • downloading File from server with the link
  • getting list of Files’ information (file name & url)

These are APIs to be exported:

Methods Urls Actions
POST /upload upload a File
GET /files get List of Files (name & url)
GET /files/[filename] download a File

This is the static folder that stores all uploaded files:

spring-boot-multipart-file-upload-example-static-folder

For deleting file, kindly visit:
Spring Boot Delete File example

If you want to store files in database like this:
spring-boot-upload-files-to-database-table-files

You can find instruction at:
Spring Boot Upload/Download File to/from Database example

Technology

  • Java 17 / 11 / 8
  • Spring Boot 3 / 2 (with Spring Web MVC)
  • Maven

Project Structure

spring-boot-multipart-file-upload-example-project-structure

Let me explain it briefly.

FileInfo contains information of the uploaded file.
FilesStorageService helps us to initialize storage, save new file, load file, get list of Files’ info, delete all files.
FilesController uses FilesStorageService to export Rest APIs: POST a file, GET all files’ information, download a File.
FileUploadExceptionAdvice handles exception when the controller processes file upload.
application.properties contains configuration for Servlet Multipart.
uploads is the static folder for storing files.
pom.xml for Spring Boot dependency.

Setup Spring Boot project

Use Spring web tool or your development tool (Spring Tool Suite, Eclipse, Intellij) to create a Spring Boot project.

Then open pom.xml and add these dependencies:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>

Create Service for File Storage

First we need an interface that will be autowired in the Controller.
In service folder, create FilesStorageService interface like following code:

service/FilesStorageService.java

package com.bezkoder.spring.files.upload.service;

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

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

public interface FilesStorageService {
  public void init();

  public void save(MultipartFile file);

  public Resource load(String filename);

  public void deleteAll();

  public Stream<Path> loadAll();
}

Now we create implementation of the interface.

service/FilesStorageServiceImpl.java

package com.bezkoder.spring.files.upload.service;

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.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 FilesStorageServiceImpl implements FilesStorageService {

  private final Path root = Paths.get("uploads");

  @Override
  public void init() {
    try {
      Files.createDirectories(root);
    } catch (IOException e) {
      throw new RuntimeException("Could not initialize folder for upload!");
    }
  }

  @Override
  public void save(MultipartFile file) {
    try {
      Files.copy(file.getInputStream(), this.root.resolve(file.getOriginalFilename()));
    } catch (Exception e) {
      if (e instanceof FileAlreadyExistsException) {
        throw new RuntimeException("A file of that name already exists.");
      }

      throw new RuntimeException(e.getMessage());
    }
  }

  @Override
  public Resource load(String filename) {
    try {
      Path file = root.resolve(filename);
      Resource resource = new UrlResource(file.toUri());

      if (resource.exists() || resource.isReadable()) {
        return resource;
      } else {
        throw new RuntimeException("Could not read the file!");
      }
    } catch (MalformedURLException e) {
      throw new RuntimeException("Error: " + e.getMessage());
    }
  }

  @Override
  public void deleteAll() {
    FileSystemUtils.deleteRecursively(root.toFile());
  }

  @Override
  public Stream<Path> loadAll() {
    try {
      return Files.walk(this.root, 1).filter(path -> !path.equals(this.root)).map(this.root::relativize);
    } catch (IOException e) {
      throw new RuntimeException("Could not load the files!");
    }
  }
}

Define Data Models

Let’s create FileInfo model which has fields: name & url.

model/FileInfo.java

package com.bezkoder.spring.files.upload.model;

public class FileInfo {
  private String name;
  private String url;

  public FileInfo(String name, String url) {
    this.name = name;
    this.url = url;
  }

  public String getName() {
    return this.name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public String getUrl() {
    return this.url;
  }

  public void setUrl(String url) {
    this.url = url;
  }
}

Define Response Message

The ResponseMessage is for message to client that we’re gonna use in Rest Controller and Exception Handler.

message/ResponseMessage.java

package com.bezkoder.spring.files.upload.message;

public class ResponseMessage {
  private String message;

  public ResponseMessage(String message) {
    this.message = message;
  }

  public String getMessage() {
    return message;
  }

  public void setMessage(String message) {
    this.message = message;
  }

}

Create Controller for upload & download Files

In controller package, we create FilesController.

controller/FilesController.java

package com.bezkoder.spring.files.upload.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.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;

import com.bezkoder.spring.files.upload.model.FileInfo;
import com.bezkoder.spring.files.upload.model.ResponseMessage;
import com.bezkoder.spring.files.upload.service.FilesStorageService;

@Controller
@CrossOrigin("http://localhost:8081")
public class FilesController {

  @Autowired
  FilesStorageService storageService;

  @PostMapping("/upload")
  public ResponseEntity<ResponseMessage> uploadFile(@RequestParam("file") MultipartFile file) {
    String message = "";
    try {
      storageService.save(file);

      message = "Uploaded the file successfully: " + file.getOriginalFilename();
      return ResponseEntity.status(HttpStatus.OK).body(new ResponseMessage(message));
    } catch (Exception e) {
      message = "Could not upload the file: " + file.getOriginalFilename() + ". Error: " + e.getMessage();
      return ResponseEntity.status(HttpStatus.EXPECTATION_FAILED).body(new ResponseMessage(message));
    }
  }

  @GetMapping("/files")
  public ResponseEntity<List<FileInfo>> getListFiles() {
    List<FileInfo> fileInfos = storageService.loadAll().map(path -> {
      String filename = path.getFileName().toString();
      String url = MvcUriComponentsBuilder
          .fromMethodName(FilesController.class, "getFile", path.getFileName().toString()).build().toString();

      return new FileInfo(filename, url);
    }).collect(Collectors.toList());

    return ResponseEntity.status(HttpStatus.OK).body(fileInfos);
  }

  @GetMapping("/files/{filename:.+}")
  @ResponseBody
  public ResponseEntity<Resource> getFile(@PathVariable String filename) {
    Resource file = storageService.load(filename);
    return ResponseEntity.ok()
        .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + file.getFilename() + "\"").body(file);
  }
}

@CrossOrigin is for configuring allowed origins.
@Controller annotation is used to define a controller.
@GetMapping and @PostMapping annotation is for mapping HTTP GET & POST requests onto specific handler methods:

  • POST /upload: uploadFile()
  • GET /files: getListFiles()
  • GET /files/[filename]: getFile()

– We use @Autowired to inject implementation of FilesStorageService bean to local variable.

Configure Multipart File for Servlet

Let’s define the maximum file size that can be uploaded in application.properties as following:

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

spring.servlet.multipart.max-file-size: max file size for each request.
spring.servlet.multipart.max-request-size: max request size for a multipart/form-data.

Handle File Upload Exception

This is where we handle the case in that a request exceeds Max Upload Size. The system will throw MaxUploadSizeExceededException and we’re gonna use @ControllerAdvice with @ExceptionHandlerannotation for handling the exceptions.

exception/FileUploadExceptionAdvice.java

package com.bezkoder.spring.files.upload.exception;

import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

import com.bezkoder.spring.files.upload.model.ResponseMessage;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

@ControllerAdvice
public class FileUploadExceptionAdvice extends ResponseEntityExceptionHandler {

  @ExceptionHandler(MaxUploadSizeExceededException.class)
  public ResponseEntity<ResponseMessage> handleMaxSizeException(MaxUploadSizeExceededException exc) {
    return ResponseEntity.status(HttpStatus.EXPECTATION_FAILED).body(new ResponseMessage("File too large!"));
  }
}

Initialize Storage

We need to run init() method of FilesStorageService (and also deleteAll() if necessary). So open SpringBootUploadFilesApplication.java and implement CommandLineRunner for run() method like this:

package com.bezkoder.spring.files.upload;

// import javax.annotation.Resource; // for Spring Boot 2
import jakarta.annotation.Resource;

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

import com.bezkoder.spring.files.upload.service.FilesStorageService;

@SpringBootApplication
public class SpringBootUploadFilesApplication implements CommandLineRunner {
  @Resource
  FilesStorageService storageService;

  public static void main(String[] args) {
    SpringApplication.run(SpringBootUploadFilesApplication.class, args);
  }

  @Override
  public void run(String... arg) throws Exception {
//    storageService.deleteAll();
    storageService.init();
  }
}

Run & Test

Run Spring Boot application with command: mvn spring-boot:run.
Refresh the project directory and you will see uploads folder inside it.

Let’s use Postman to make some requests.

– Upload some files:

spring-boot-multipart-file-upload-example-upload-files

– Upload a file with size larger than max file size (500KB):

spring-boot-multipart-file-upload-example-upload-exceed-max-size

– Check uploads folder:

spring-boot-multipart-file-upload-example-uploads-folder

– Retrieve list of Files’ information:

spring-boot-multipart-file-upload-example-get-list-files

– Now you can download any file from one of the paths above.
For example: http://localhost:8080/files/bezkoder.png.

– If you upload a file that the file name already exists:

spring-boot-file-upload-rest-api-example-check-file-exist

Source Code

You can find the complete source code for this tutorial on Github.

With Template Engine:
Spring Boot Thymeleaf File Upload example

Or Reactive Web:
Spring WebFlux File upload example

Conclusion

Today we’ve learned how to create Spring Boot File Upload Rest Api Application to upload multipart files and get files’ information with static folder via Restful API.

Following tutorials explain how to build Front-end Apps to work with our Spring Boot Server:
Angular 8 / Angular 10 / Angular 11 / Angular 12 / Angular 13 / Angular 14 / Angular 15
Angular Material 12
Vue Client / Vuetify Client
React Client / React Hooks Client
React Image Upload with Preview
Material UI Client
Axios Client

For deleting file:
Spring Boot Delete File example

Or upload multiple Files at once:
How to upload multiple files in Java Spring Boot

You can also know way to upload an Excel/CSV file and store the content in MySQL database with the post:
Spring Boot: Upload/Import Excel file data into MySQL Database
Spring Boot: Upload/Import CSV file data into MySQL Database

If you want to store files in database like this:
spring-boot-upload-files-to-database-table-files

You can find instruction at:
Spring Boot Upload/Download File to/from Database example

Happy Learning! See you again.

Further Reading

58 thoughts to “Spring Boot File upload example with Multipart File”

  1. Thank u so much, I have a question. Is this available in real world app? For example, when I deploy this Spring Boot app and if users calls the URL to get image files, can they see the images stored in my host’s static folder?

  2. ok… Postman eventually displayed the message

    “message”: “Uploaded the file successfully: ToastCode.txt”

    I cannot find the file on my PC ! Came from the C: root directory and went ??????
    Any further attempt to upload the file thru Postman returns:
    “message”: “Could not upload the file: ToastCode.txt!”

    Seems like you can upload once and it will not overwrite…OK, but I still cannot find the files. 🙁

    I was also looking for a fodler called ‘uploads’ as referenced in the code.

    Rob

  3. Bezkoder,
    Thank you for your tutorial. I am very excited to get this working. Needed for a project I developing. I will eventually use my React Front end to select and send files.

    I have imported from GITHIB and changed to @CrossOrigin(“http://localhost:8080”)
    I selected a file on my C:\ drive and in Postman under content type entered ‘multipart/form-data’
    post : http://localhost:8080/upload

    Tomcat started on port(s): 8080 (http) with context path ”

    I entered the POSTMAN example and get a 400 bad request.

    How can I help you, help me, get this resolved?

    Thank you in advance,
    Rob

  4. Hello, thank you for such a great tutorial .But in my case i need to send some extra information long with file. So how can i do it in post api ?

  5. Hi,

    Thanks for the work. Appreciate it.

    Just 1 issue, the same code will not work for file upload after few days running in the server. But, after we have restarted the application, it will work back until a couple of days later.

  6. instead of storing it in the uploads folder, how can I store it in another path? say i wanna store it in a shared path

  7. Hi ! Great tutorial as always !
    I have an issue with the “getListFiles” method from the FIlesController class.
    When testing with Postamn, the upload works fine. But the “getListFiles” returns an “incorrect” url :

    “name”: “testTxt.txt”,
    “url”: “http://localhost:8080/api/files/{filename:.+}” instead of “…localhost:8080/api/files/testTxt.txt”
    (@RequestMapping(“/api”) in the controller)

    I have no error on compilation nor in the response body (http status OK).
    I have tried everything that came to my mind, I have double checked the syntax…
    Do you have any idea ?

    Thank you !

  8. Hi,
    First, thank you for this great tutorial.
    How can i change the folder where the files are saved ? For example, if i want to save the files in a HDD ? Can i write an absolute path instead of “Paths.get(“uploads”);” ?

    Best regards

  9. Whenever I try to remove the deleteAll() method from main function, I get the below error
    java.lang.IllegalStateException: Failed to execute CommandLineRunner
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:798) ~[spring-boot-2.3.1.RELEASE.jar:2.3.1.RELEASE]
    at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:779) ~[spring-boot-2.3.1.RELEASE.jar:2.3.1.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:322) ~[spring-boot-2.3.1.RELEASE.jar:2.3.1.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1237) ~[spring-boot-2.3.1.RELEASE.jar:2.3.1.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226) ~[spring-boot-2.3.1.RELEASE.jar:2.3.1.RELEASE]
    at com.servyces.churchportal.ChurchportalApplication.main(ChurchportalApplication.java:16) ~[classes/:na]

    With deleteAll() it works perfectly, but without it, it throws the said error. Can you help me resolve this?

    1. You can simply pass exact filename (including path) to the “FileSystemUtils.deleteRecursively()” method.

      Here’s a sample-

          @Override
          public void deleteAll() {
              FileSystemUtils.deleteRecursively(root.toFile());
          }
      
          @Override
          public void delete(String filename) {
              try {
                      FileSystemUtils.deleteRecursively(root.resolve(filename));
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }
      

      ---------------------------------
      I have mentioned deleteAll() method just for reference.
      This operation can throw error, so you need to perform some exception handling as well.

  10. Hi, I leave the comment rare, but you did a great job. Thank you a lot for your series, you have done really a great job.

  11. Invalid CORS request is comming in Postman, I enabled postman interceptor + enabled allow origin header but still facing same issue for POST req , working fine for Get request

  12. Thank you very much for your reply 🙂
    I am also trying to display a list of images (not only their names , the image it self ) from database with their path saved in uploads file (I kind of combined your tutorial with another to make the Url, name and size saved in database) and I tried so many spring boot and angular tutoriels that would go with this tutoriel but in vain .. so do you have an idea or a tutoriel that shows how to display a list of images from database and uploads ?

    1. Hi, maybe you want to display a list of images (not only their names , the image it self ) in uploads folder with their information (Url, name and size) saved in database?

      I will write the tutorial when having time. 🙂

  13. Hi,

    First of all thanks for sharing nice and easy tutorial.
    Only request I have is to add security prospect in it like validations, check file format etc.
    When we upload the files from the internet these are the basic things we required now.

    Thanks,
    Atul

  14. Whenever I close Eclipse or reload the service in which I created the file uploads, the file becomes empty.. why is that ?? Also my friend tried your method too and she dosn’t have the same issue. Why in your opinion the uploads file becomes empty?

    1. Hi, look at SpringBootUploadFilesApplication.java, in run() method, we call storageService.deleteAll(); for deleting all stored files.
      So you only need to remove the line. 🙂

  15. When hitting postman request when you enter key in body -> form/data and hover over inside key box then you will see File option in addition to text.
    Select File option and choose a file and run the program it will work as expected.

  16. i am getting 400 bad request, used the same input params in the form data for the body section .

    can you shares the headers you are using? can we leave them as default ?

      1. yes
        i have that in my headers, I wish I could share my screenshot here, still getting 400 bad request “The request cannot be fullfilled due to bad syntax”

  17. hi, thk u very muth for this tutoriel
    i use the DataBAse postresql , in postman i try insert with post request ( my class with many attributes name, age , … and this for file upload) that’s ok response : 200 in postman
    but i not find the file in DB ?? (base empty) however the request is ok

    1. You can simply pass exact filename (including path) to the “FileSystemUtils.deleteRecursively()” method.

      Here’s a sample-

          @Override
          public void deleteAll() {
              FileSystemUtils.deleteRecursively(root.toFile());
          }
      
          @Override
          public void delete(String filename) {
              try {
                      FileSystemUtils.deleteRecursively(root.resolve(filename));
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }
      

      ---------------------------------
      I have mentioned deleteAll() method just for reference.
      This operation can throw error, so you need to perform some exception handling as well.

  18. Hi !! I see that the files are saved in a project directory. Is there any chance to save them in a sql database?

  19. I get a “400 Bad Request” when testing the api with post man but i did everything like you ,it worked at the start but now it doesn’t any help please.

    1. I had the same issue, then after checking the code several times, then I had to make below changes to fix it.

      1. In the FileController.java , i have modified the below line
      //@CrossOrigin(“http://localhost:8081”) //old line
      @CrossOrigin(origins = “*”, maxAge = 3600) //new line

      2. under the post tool, i had to type explicitly enter below new line under headers. Mr.Bezkoder already mentioned about it. After this my Header count showed 9.
      content-type: multipart/form-data

      3. post tool, under body tab, the key value should be typed explicitly as “file”, it is little deceiving, it would show “file/text” before an entry.

      Otherwise it works great. Thank you Bezkoder for the great tutorial.

  20. Thank you so much this was so usefull for my project. Do you know I could delete a file?? thanks you.

    1. You can simply pass exact filename (including path) to the “FileSystemUtils.deleteRecursively()” method.

      Here’s a sample-

          @Override
          public void deleteAll() {
              FileSystemUtils.deleteRecursively(root.toFile());
          }
      
          @Override
          public void delete(String filename) {
              try {
                      FileSystemUtils.deleteRecursively(root.resolve(filename));
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }
      

      ---------------------------------
      I have mentioned deleteAll() method just for reference.
      This operation can throw error, so you need to perform some exception handling as well.

    1. Hi, you can use @RequestParam MultipartFile[] file.
      I will write the tutorial when having time. Thanks for your suggestion.

    2. Hi, I’ve just written a new Tutorial for Multiple Files Upload at once. You can find it in the Conclusion section 🙂

  21. It’s not working for me can you help me.

    [org.springframework.web.multipart.support.MissingServletRequestPartException: Required request part ‘file’ is not present]

    1. Hi, please make sure that you’ve set correct form data (look at my Postman example) 🙂

  22. But the methode, delete All will cause deleting the uploaded files also and if we delete it… we will have a command liner problem caused of this action, so how to avoid this problem? because I need a solution that maintain the creation of the directory automatically and also maintaining the old uploaded files

      1. Hello !

        First of all thanks a lot for the post. Could you also provide us an example in Spring of how to upload a file and store it in memory? Or maybe if you can guide us on the internet on how to find it.
        Thank you !

Comments are closed to reduce spam. If you have any question, please send me an email.