Multipart file upload with RestTemplate

Upasana | September 05, 2020 | 4 min read | 6,151 views


In this short tutorial we will learn how to do file upload to a spring mvc endpoint using RestTemplate via ByteArrayResource & FileSystemResource. We will be using Spring Boot 2.x for this tutorial along with Gradle build script.

We will cover two topics here:

  • Creating a multipart file upload controller

  • Creating RestTemplate client for file upload

Part 1. Multipart file upload Controller

We will create a sample Spring Boot project using https://start.spring.io and edit the build.gradle for the following changes:

build.gradle
plugins {
	id 'org.springframework.boot' version '2.3.3.RELEASE'
	id 'java'
}

apply plugin: 'io.spring.dependency-management'

group = 'com.shunya'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-web'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

Now we need to configure Spring Boot for file upload using application.yml

/src/main/resources/application.yml
spring:
  application:
    name: file-upload

  main:
    web-application-type: servlet

  mvc:
    async:
      request-timeout: -1
  servlet:
    multipart:
      max-file-size: 20MB
      max-request-size: 20MB
      enabled: true
      file-size-threshold: 1MB
      location: ${java.io.tmpdir} (1)
1 Intermediate location for storing file uploads on server side

Now we are all set to write a simple file upload controller that will accept a multi-part file and save it on server side.

/com/shunya/tutorials/UploadController.java
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;

@RestController
@RequestMapping("/upload")
public class UploadController {

    private static final Logger logger = LoggerFactory.getLogger(UploadController.class);

    @RequestMapping(method = RequestMethod.POST)
    public ServiceResponse handleFileUpload(@RequestParam("user-file") MultipartFile multipartFile) throws IOException {
        String name = multipartFile.getOriginalFilename();
        logger.info("File name: " + name);
        //Ideally you shall read bytes using multipartFile.getInputStream() and store it appropriately
        byte[] bytes = multipartFile.getBytes();
        logger.info("File uploaded content: {}", new String(bytes));
        ServiceResponse response = new ServiceResponse();
        response.setSuccess(true);
        return response;
    }
}

Part 2. Using ByteArrayResource with RestTemplate for file upload

We can use either FileSystemResource or ByteArrayResource for fileupload with RestTemplate, we will cover both one by one.

In the below code snippet, we are writing a Spring Boot Junit Testcase that will start the container and do the file upload using RestTemplate.

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.http.*;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

import static java.util.Arrays.asList;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.IsEqual.equalTo;

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ApplicationTests {

	@Autowired
	private TestRestTemplate testRestTemplate;  (3)

	@Test
	public void testFileUploadByteArrayResource() {
		String fileName = "test.txt";
		byte[] fileContent = "this is file content".getBytes();

		HttpHeaders parts = new HttpHeaders();  (1)
		parts.setContentType(MediaType.TEXT_PLAIN);
		final ByteArrayResource byteArrayResource = new ByteArrayResource(fileContent) {
			@Override
			public String getFilename() {   (2)
				return fileName;
			}
		};
		final HttpEntity<ByteArrayResource> partsEntity = new HttpEntity<>(byteArrayResource, parts);

		HttpHeaders headers = new HttpHeaders();
		headers.setAccept(asList(MediaType.APPLICATION_JSON));
		headers.setContentType(MediaType.MULTIPART_FORM_DATA);

		MultiValueMap<String, Object> requestMap = new LinkedMultiValueMap<>();
		requestMap.add("user-file", partsEntity);

		final ParameterizedTypeReference<ServiceResponse> typeReference = new ParameterizedTypeReference<ServiceResponse>() {
		};

		final ResponseEntity<ServiceResponse> exchange = testRestTemplate.exchange("/upload", HttpMethod.POST, new HttpEntity<>(requestMap, headers), typeReference);
		if(exchange.getStatusCode().is2xxSuccessful()) {
			System.out.println("File uploaded = " + exchange.getBody().isSuccess());
		}
		assertThat(exchange.getStatusCode().is2xxSuccessful(), equalTo(true));
		assertThat(exchange.getBody().isSuccess(), equalTo(true));
	}

}
1 We are setting mime type for individual files that we add to the request.
2 Overriding getFilename() is necessary step here, because we need it on server side. Overriding this method is not required if we use FileSystemResource.
3 We can use either TestRestTemplate of RestTemplate. For production code, you shall use RestTemplate only.

Part 3. Using FileSystemResource with RestTemplate for file upload

In this last section, we will be using FileSystemResource instead of ByteArrayResource for handlign file upload with TestRestTemplate.

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ApplicationTests {

	@Autowired
	private TestRestTemplate testRestTemplate;

	@Test
	public void testFileUploadFileSystemResource() throws IOException {
		byte[] fileContent = "this is file content".getBytes();

		HttpHeaders headers = new HttpHeaders();
		headers.setAccept(asList(MediaType.APPLICATION_JSON));
		headers.setContentType(MediaType.MULTIPART_FORM_DATA);

		MultiValueMap<String, Object> requestMap = new LinkedMultiValueMap<>();
		requestMap.add("user-file", createTempFileResource(fileContent));

		final ParameterizedTypeReference<ServiceResponse> typeReference = new ParameterizedTypeReference<ServiceResponse>() {
		};

		final ResponseEntity<ServiceResponse> exchange = testRestTemplate.exchange("/upload", HttpMethod.POST, new HttpEntity<>(requestMap, headers), typeReference);
		if(exchange.getStatusCode().is2xxSuccessful()) {
			System.out.println("File uploaded = " + exchange.getBody().isSuccess());
		}
		assertThat(exchange.getStatusCode().is2xxSuccessful(), equalTo(true));
		assertThat(exchange.getBody().isSuccess(), equalTo(true));
	}

	static Resource createTempFileResource(byte [] content) throws IOException {
		Path tempFile = Files.createTempFile("upload-file", ".txt");
		Files.write(tempFile, content);
		return new FileSystemResource(tempFile.toFile());
	}

}

That’s it.


Top articles in this category:
  1. Spring Webclient multipart file upload
  2. Spring Boot multipart file upload server
  3. S3 File upload & download with AWS Java SDK v2
  4. Download a file using Spring RestTemplate
  5. Spring RestTemplate Basic Authentication
  6. SendGrid Attachments with Spring Boot
  7. Dialoglfow fulfillment with Spring Boot

Recommended books for interview preparation:

Find more on this topic:
Buy interview books

Java & Microservices interview refresher for experienced developers.