Hero image

How to upload a file in Spring Boot

Nov 27, 2024
Spring Boot Java

In this article, we’ll create a Spring Boot application with an endpoint that accepts files.

Check out the full example on GitHub https://github.com/minibuildsio/spring-boot-file-upload-example.

Adding an endpoint that accepts a file

To add an endpoint the accepts a file create an endpoint that has a MultipartFile as one of the input arguments. The MultipartFile class represents the file and has methods to get the content type, size, contents, etc.

This example simply logs some details about the file and responds with the contents of the file, however, you could refuse certain file types by checking file.getContentType(). For example, if you only wanted to accept jpegs you could check whether file.getContentType() is MediaType.IMAGE_JPEG_VALUE which equals “image/jpeg” and return an error when the content type isn’t a jpeg.

@RestController
public class FileUploadController {

    Logger logger = LoggerFactory.getLogger(FileUploadController.class);

    @PostMapping("/upload")
    public Map<String, String> uploadFile(@RequestParam("file") MultipartFile file)
            throws IOException {
        logger.info("File content type: {}", file.getContentType());
        logger.info("File size: {}", file.getSize());

        return Map.of("contents", new String(file.getBytes()));
    }
}

Limiting the size of files that can be uploaded

Often you’ll want to limit the size of the file that can be uploaded this can be achieve using the configuration below. max-file-size is the max size of an individual files and max-request-size is the max size of the total request.

spring:
    servlet:
        multipart:
            enabled: true
            max-file-size: 1MB
            max-request-size: 1MB

How write a test that upload a file

To test uploading a file we can spin up the application on a random port and make calls it using to TestRestTemplate. To pass a file in a request we can set the content type to MediaType.MULTIPART_FORM_DATA and the body to map containing a ByteArrayResource in the body.

Below are two tests one that uploads a file containing “hello world” and checks that the response is “hello world”. The other uploads a file that is too large and checks that the response is a 413 payload too large.

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class FileUploadExampleApplicationTests {

    private static final ParameterizedTypeReference<Map<String, String>> MAP_TYPE =
            new ParameterizedTypeReference<>() {
            };

    @Autowired
    TestRestTemplate restTemplate;

    @Test
    void when_a_file_is_uploaded_the_response_should_contain_the_contents_of_the_file() {
        // Given a multipart request containing a small file
        ByteArrayResource file = createFile("hello world");
        RequestEntity<MultiValueMap<String, Object>> requestEntity = createMultipartRequest(file);

        // When the request is made
        ResponseEntity<Map<String, String>> response = restTemplate.exchange(
                requestEntity, MAP_TYPE
        );

        // Then the response status code is ok
        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);

        // And the response contains the file contents
        assertThat(response.getBody()).containsEntry("contents", "hello world");
    }
  
    @Test
    void when_a_file_is_uploaded_that_is_too_large_the_response_should_be_payload_too_large() {
        // Given a multipart request containing a large file (>1MB)
        ByteArrayResource file = createFile("a".repeat(1_100_000));
        RequestEntity<MultiValueMap<String, Object>> requestEntity = createMultipartRequest(file);

        // When the request is made
        ResponseEntity<Map<String, String>> response = restTemplate.exchange(
                requestEntity, MAP_TYPE
        );

        // Then the response status code is payload too large
        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.PAYLOAD_TOO_LARGE);
    }
}

createFile and createMultipartRequest are convenience functions.

private ByteArrayResource createFile(String contents) {
    return new ByteArrayResource(contents.getBytes()) {
        @Override
        public String getFilename() {
            return "file.txt";
        }
    };
}

private RequestEntity<MultiValueMap<String, Object>> createMultipartRequest(
        ByteArrayResource file
) {
    MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
    body.add("file", file);

    return RequestEntity.post("/upload")
            .contentType(MediaType.MULTIPART_FORM_DATA)
            .body(body);
}

Uploading a file using curl

You can test the application locally using curl to upload a file, simply replace path_to_file with the path to the file you want to upload.

curl -X POST -F "file=@path_to_file" http://localhost:8080/upload