11

How to provide large files for download through spring controller ? I followed few discussions on similar topic :

Downloading a file from spring controllers

but those solutions fails for large files ~ 300mb - 600mb. I am getting OutOfMemoryException on the last line :

@RequestMapping(value = "/file/{dummyparam}.pdf", method = RequestMethod.GET, produces=MediaType.APPLICATION_OCTET_STREAM_VALUE) public @ResponseBody byte[] getFile(@PathVariable("dummyparam") String dummyparam, HttpServletResponse response) { . . InputStream is = new FileInputStream(resultFile); response.setHeader("Content-Disposition", "attachment; filename=\"dummyname " + dummyparam + ".pdf\""); . . return IOUtils.toByteArray(is); 

My (naive) assumption was that IOUtils will handle even large files but this is not obviously happening. Is there any way how to split file into chunks as download is in progress ? Files are usually around 300 - 600mb large. Max number of concurrent downloads is estimated to 10.

Easy way would be to link files as static content in the webserver directory but we would like to try do it in within our Spring app.

3 Answers 3

23

It is because you are reading the entire file into memory, use a buffered read and write instead.

@RequestMapping(value = "/file/{dummyparam}.pdf", method = RequestMethod.GET, produces=MediaType.APPLICATION_OCTET_STREAM_VALUE) public void getFile(@PathVariable("dummyparam") String dummyparam, HttpServletResponse response) { InputStream is = new FileInputStream(resultFile); response.setHeader("Content-Disposition", "attachment; filename=\"dummyname " + dummyparam + ".pdf\""); int read=0; byte[] bytes = new byte[BYTES_DOWNLOAD]; OutputStream os = response.getOutputStream(); while((read = is.read(bytes))!= -1){ os.write(bytes, 0, read); } os.flush(); os.close(); } 
Sign up to request clarification or add additional context in comments.

2 Comments

P what should be the size of BYTES_DOWNLOAD in the above implementation
Use instead, IOUtils.copyLarge(inputStream, response.getOutputStream()); where buffer size is given by DEFAULT_BUFFER_SIZE. Size may vary from 4KB to 8KB based on the jar version.
1

For Spring , Need use InputStreamResource class in ResponseEntity .

Demo Code :

 MediaType mediaType = MediaTypeUtils.getMediaTypeForFileName(this.servletContext, fileName); System.out.println("fileName: " + fileName); System.out.println("mediaType: " + mediaType); File file = new File(DIRECTORY + "/" + fileName); InputStreamResource resource = new InputStreamResource(new FileInputStream(file)); return ResponseEntity.ok() // Content-Disposition .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + file.getName()) // Content-Type .contentType(mediaType) // Contet-Length .contentLength(file.length()) // .body(resource); } 

Ref Link : https://o7planning.org/en/11765/spring-boot-file-download-example

1 Comment

InputStreamResource is one of the most generic implementations of Spring's Resource interface and made only for wrapping already opened streams, the process of which creation we cannot control. I'd rather consider using ClassPathResource or FileSystemResource depending on the file location in the file system.
0

You can also use Spring WebFlux's reactive programming features for downloading large files.

Yes, it is possible to use Spring WebFlux reactive web client in your Spring MVC controllers.

See my answer to a similar question.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.