My code was initially breaking if I try and push a large file (anything above 1MB size). It is working fine now and able to accommodate the file sizes I want by adding the following in the properties file.
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB
But how can I write a proper unit/integration test on this to ensure it allows file size up to 10MB?
The following has a good test example (the accepted answer) but it is using a mock file setup to test.
Using Spring MVC Test to unit test multipart POST request
Is there a way I could mock and specify file size?
Or actually pass in a real large file for testing (preferably not)?
Or a better way to do this, test I can accept a large file up to 10MB?
This is the method to be tested
#PostMapping(path = "/example", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<SomeResponse> upload(#PathVariable(#RequestPart("file") MultipartFile file) {
//we won't even get inside thi method and would fail if the file size is over 1MB previously.
// It works currently when I add files with size above 1MB
// cos I added the above 2 lines (spring.servlet.... in the properties file)
// some logic which works fine.
SomeResponse obj = //
return new ResponseEntity<>(obj, HttpStatus.OK);
}
This is current test (and there are other tests to test negative scenarios)
#Test
public void testValidUpload() throws Exception {
String fileContents = "12345";
String expectedFileContents = "12345\nSomeData";
mockServer.expect(requestTo("http://localhost:8080/example"))
.andExpect(method(HttpMethod.POST))
.andExpect(expectFile("file", "test.csv", expectedFileContents))
.andRespond(withStatus(HttpStatus.OK)
.contentType(MediaType.TEXT_PLAIN)
.body("done")
);
String response = this.mvc.perform(multipart("/example")
.file(new MockMultipartFile("file", "filename.csv", MediaType.TEXT_PLAIN_VALUE, fileContents.getBytes())))
.andExpect(status().isOk())
.andExpect(content().contentType(APPLICATION_JSON))
.andExpect(jsonPath("responseStatusCode", Matchers.equalTo("200")))
.andExpect(jsonPath("httpStatus", Matchers.equalTo("OK")))
.andReturn().getResponse().getContentAsString();
Response response = objectMapper.readValue(response, Response.class);
assertEquals(HttpStatus.OK, response.getHttpStatus());
assertEquals(5, response.id());
}
You can try something like this:
byte[] bytes = new byte[1024 * 1024 * 10];
MockMultipartFile firstFile = new MockMultipartFile("data", "file1.txt", "text/plain", bytes);
See documentation.
You can also refer to this article.
Related
Using intellij, Java and restassured:
I am sending the request as needed and I added this to my code:
public static Response PostInstinctQuery() throws IOException, ParseException {
PrintStream fileOutPutStream = new PrintStream(new File("request_log.txt"));
config = config().logConfig(new LogConfig().defaultStream(fileOutPutStream));
RestAssured.baseURI = BASEURI;
RequestSpecification request = RestAssured.given();
//Headers
request.header("Key",key);
request.body(getJson());
request.log().all();
Response response = request.post(PATH);
return response;
}
That is save the request in "request_log.txt" file.
so what is the problem ? the file shows the same request over and over.
If I use TestNG diff data then I would expect that the file will contain all the diff request.
And I really want the request to be able to save in a String variable for assert / report purposes also.
Thanks!
I think you can use RequestLoggingFilter to config one time, no need log().all().
OutputStream file = new FileOutputStream("request_log.txt");
PrintStream stream = new PrintStream(file, true);
RestAssured.filters(RequestLoggingFilter.logRequestTo(stream));
I'm wondering if there a real difference when a Sprin MVC controller method returns byte array byte[] to represent a downloaded file or when I copy InputStream object to the ServletOutputStream object?
The reason I'm asking is that I have to make sure that there won't be any OutOfMemory errors when downloading large files. Will the passing file through the ServletOutputStream help to avoid it?
Passing byte array:
byte[] download() {
return getUrlContentAsByteArray();
}
Passing in the ServletOutputStream:
void download(HttpServletResponse response) {
InputStream content = getUrlContentAsStream();
ServletOutputStream outputStream = response.getOutputStream();
response.reset();response.setContentType(ContentType.APPLICATION_OCTET_STREAM.getMimeType());
IOUtils.copyLarge(inputStream, outputStream);
}
In your first example, you have to read the entire response into memory and store it in a byte array. That will require at least as much memory as the size of the response.
In your second example, you do not keep the entire response in memory at once, but use IOUtils.copy to copy the content in multiple, small chunks from the source and into the servlet response. IOUtils is by default using a 4kB large buffer. You are however using a strange mix of both Spring and Servlet APIs.
Using Spring MVC alone, you can leave out the servlet API, return the InputStream wrapped as a Resource and let Spring do the copy job for you:
#RequestMapping(value = "/download", method = RequestMethod.GET)
#ResponseBody
public ResponseEntity<Resource> download() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
InputStream is = null; // get your input stream here
Resource resource = new InputStreamResource(is);
return new ResponseEntity<>(resource, headers, HttpStatus.OK);
}
I cannot find out why the mp3 file is different after download from my server than original one saved previously there.
This is my controller method. The content of file (byte[] content) is identical with original file on this stage - the original file is the same as file retrieved from database (checked in debugger).
#ResponseBody
#RequestMapping(method = RequestMethod.GET, value = "/{sampleId}/file")
public HttpEntity<byte[]> getFile(#PathVariable Long sampleId) {
ResourceEntity resourceEntity = testSampleRepository.getFile(sampleId);
byte[] content = resourceEntity.getContent();
String fileName = resourceEntity.getFileName();
HttpHeaders header = new HttpHeaders();
header.setContentType(new MediaType("audio", "mpeg"));
header.set(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=" + fileName.replace(" ", "_"));
header.setContentLength(content.length);
return new HttpEntity<byte[]>(content, header);
}
This is how files differ (the left is original one):
Why passing using HTTP distors my file? Should mediaTypes enforce certain encoding? (there was no difference with "audio/mpeg" mediaType and without it).
It should work, if you set the produces = "application/octet-stream" attribute (MediaType.APPLICATION_OCTET_STREAM). Otherwise, you are trapped by Spring's converter framework.
You may want to have a look here, seems your problem is very similar: Spring MVC: How to return image in #ResponseBody? .
My HTML file has the following command
<img src="/5/background" alt="" width="192" height="192">
Which I have bound to the following inside a #RestController in a Spring Boot application:
#RequestMapping(value = "/{connector}/background", method = RequestMethod.GET)
public File getConnectorBackgroundImage(#PathVariable("connector") int connector)
{
File image = new File("folder" + connector + "/myPicture");
return image;
}
I have done my debugging and I know the method is being entered, and I know the path is correct, but all that shows is the icon in the browser when there is a problem loading a picture.
What else am I missing?
Spring does not know how to handle the file that way. If you return a File, the controller will simply show you the path to the file if you call the REST API.
The proper way to do it is to read the file as a byte[]. Using commons-io you could come up with something like this:
File image = new File("folder" + connector + "/myPicture");
FileInputStream input = null;
try {
input = new FileInputStream(image);
return IOUtils.toByteArray(input);
} finally {
IOUtils.closeQuietly(input);
}
Another thing you shouldn't forget is to provide the mimetype. To do that you tweak the #RequestMapping annotation a bit by providing the produces property:
#RequestMapping(value = "/{connector}/background", method = RequestMethod.GET, produces = MediaType.IMAGE_PNG_VALUE)
This should do the trick.
EDIT: Didn't notice the comments, you already fixed it by yourself.
I have a Spring Boot Application with a RestController and a method which will download and pass an image:
#RestController
public class PictureController {
#RequestMapping("/picture/{id}")
public HttpEntity<byte[]> getImage(#PathVariable String id) {
logger.info("Requested picture : >> " + id + " <<");
// !! Execute code for downloading !!
// Create Headers...
// return HttpEntity<byte[]>
}
}
In the logfiles I can read that the method is executed twice and I don't know why.
If I remove the code for downloading it gets executed just once as expected.
Is it because it takes a second to download it?
The code for downloading is...
byte[] response;
try {
URL url = new URL(....);
InputStream in = new BufferedInputStream(url.openStream());
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int n = 0;
while (-1 != (n = in.read(buf))) {
out.write(buf, 0, n);
}
out.close();
in.close();
response = out.toByteArray();
I also tried several solutions like...
#RequestMapping(value = "/picture2/{id}", headers = "Accept=image/jpeg, image/jpg, image/png, image/gif")
public #ResponseBody byte[] getArticleImage2(#PathVariable String id) {
I thought maybe a problem with HttpEntity but it's the same behaviour. Works as expected without code for downloading but with downloading an image it gets executed twice.
This is a serious performance issue of my application... :(
What is the problem here?
The problem depends on the Browser which is used to test the RestController.
I'am using Firefox... and Firefox tend to get some html around an image. But the method doesn't return html and so Firefox is starting another request... also for looking for a favicon.
Internet Explorer e.g. doesn't care about it and the method is only executed once as expected!
So my problem is not a real problem because later my image delivered by the RestController will be embedded in a website which has html and a favicon.