Reputation: 81
I have a file upload Controller. I'm trying to make the max file size configurable, but I'm not able to figure out why the configuration as documented (https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-multipart-file-upload-configuration) is not being applied.
plugins {
id 'org.springframework.boot' version '2.1.4.RELEASE'
id 'java'
}
apply plugin: 'io.spring.dependency-management'
group = 'com.example'
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'
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.MultipartConfigElement;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
@Controller
public class FileUploadController {
private MultipartConfigElement multipartConfigElement;
@Autowired
public FileUploadController(MultipartConfigElement multipartConfigElement) {
this.multipartConfigElement = multipartConfigElement;
}
@PostMapping("/upload")
public void upload(@RequestParam("file") MultipartFile file) throws IOException {
InputStream inputStream = new BufferedInputStream(file.getInputStream());
// TODO something with inputStream
long fileSize = file.getSize();
boolean fileSizeLimitExceeded = fileSize > multipartConfigElement.getMaxFileSize();
return;
}
}
I expect the multipartConfigElement.getMaxFileSize() should prevent larger files getting this far and automatically return a 400 or some other type of exception.
But instead the maxFileSize seems to be completely ignored.
Upvotes: 3
Views: 1533
Reputation: 519
As commented by Andrew E, MockMvc won't replicate the max file size that you set with the properties spring.servlet.multipart.max-file-size
and spring.servlet.multipart.max-request-size
.
I managed to test the file size limite like this (adapted from here https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.testing.spring-boot-applications.with-running-server)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class RunningServerTest {
// my upload endpint requires authentication, so I need to mock the Oauth2 authorization server response
@MockBean private JwtDecoder jwtDecoder;
@Test
void shouldNotUploadMultipartFileGreaterThanMaxAllowedSize(@Autowired WebTestClient webClient) {
// GIVEN
Jwt jwt =
Jwt.withTokenValue("some-bearer-token")
.header("key", "value")
.claim("email", "[email protected]")
.build();
when(jwtDecoder.decode("some-bearer-token")).thenReturn(jwt);
ClassPathResource res = new ClassPathResource("file-bigger-than-max-size.txt");
// WHEN + THEN
webClient
.post()
.uri("/upload")
.header("Authorization", "Bearer some-bearer-token")
.header("Content-Type", MediaType.MULTIPART_FORM_DATA_VALUE)
.body(BodyInserters.fromResource(res))
.exchange()
.expectStatus()
// you might need to define a custom exception handler
// to map org.apache.tomcat.util.http.fileupload.impl.SizeLimitExceededException
// to a 400 http error
.isEqualTo(HttpStatus.BAD_REQUEST);
}
}
Upvotes: 0
Reputation: 81
So it turns out that the limits do work, and do automatically throw Exceptions.
I saw this when I ran a request against my Controller using Postman.
{
"timestamp": "2019-04-05T09:52:39.839+0000",
"status": 500,
"error": "Internal Server Error",
"message": "Maximum upload size exceeded; nested exception is java.lang.IllegalStateException: org.apache.tomcat.util.http.fileupload.FileUploadBase$FileSizeLimitExceededException: The field file exceeds its maximum permitted size of 1 bytes.",
"path": "/upload"
}
The reason I wasn't seeing that was because I was testing with MockMVC (snippet below). MockMVC doesn't seem to trigger the exceptions for some reason – maybe because it is not running on a compatible web server. Probably related to https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html#spring-mvc-test-vs-end-to-end-integration-tests.
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import java.io.FileInputStream;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = DemoApplication.class)
@AutoConfigureMockMvc
public class FileUploadTest {
@Autowired
private MockMvc mockMvc;
@Test
public void givenAFileThatExceedsTheLimit_whenUploaded_responseWith400Error() throws Exception {
MockMultipartFile file =
new MockMultipartFile("file", new FileInputStream(TestUtils.loadLargeFile()));
this.mockMvc
.perform(MockMvcRequestBuilders.multipart("/upload").file(file)
.contentType(MediaType.MULTIPART_FORM_DATA_VALUE))
.andExpect(status().isBadRequest());
}
}
Upvotes: 4