Download Large file from server using REST template Java Spring MVC
Here is how I do it. Based on hints from this Spring Jira issue.
RestTemplate restTemplate // = ...;
// Optional Accept header
RequestCallback requestCallback = request -> request.getHeaders()
.setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM, MediaType.ALL));
// Streams the response instead of loading it all in memory
ResponseExtractor<Void> responseExtractor = response -> {
// Here I write the response to a file but do what you like
Path path = Paths.get("some/path");
Files.copy(response.getBody(), path);
return null;
};
restTemplate.execute(URI.create("www.something.com"), HttpMethod.GET, requestCallback, responseExtractor);
From the aforementioned Jira issue:
Note that you cannot simply return the InputStream from the extractor, because by the time the execute method returns, the underlying connection and stream are already closed.
Update for Spring 5
Spring 5 introduced the WebClient
class which allows asynchronous (e.g. non-blocking) http requests. From the doc:
By comparison to the RestTemplate, the WebClient is:
- non-blocking, reactive, and supports higher concurrency with less hardware resources.
- provides a functional API that takes advantage of Java 8 lambdas.
- supports both synchronous and asynchronous scenarios.
- supports streaming up or down from a server.
To get WebClient
in Spring Boot, you need this dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
For the moment, I'm sticking with RestTemplate
because I don't want to pull in another dependency only to get access to WebClient
.
Spring MVC : large files for download, OutOfMemoryException
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();
}
Downloading huge files from spring boot REST service
The code of the RestController is working as expected with a small memory footprint (the file is never loaded to the main memory completely).
The client code that works for me (also with a small memory footprint) can be found here.
download a file from Spring boot rest service
Option 1 using an InputStreamResource
Resource implementation for a given InputStream.
Should only be used if no other specific Resource implementation is > applicable. In particular, prefer ByteArrayResource or any of the file-based Resource implementations where possible.
@RequestMapping(path = "/download", method = RequestMethod.GET)
public ResponseEntity<Resource> download(String param) throws IOException {
// ...
InputStreamResource resource = new InputStreamResource(new FileInputStream(file));
return ResponseEntity.ok()
.headers(headers)
.contentLength(file.length())
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(resource);
}
Option2 as the documentation of the InputStreamResource suggests - using a ByteArrayResource:
@RequestMapping(path = "/download", method = RequestMethod.GET)
public ResponseEntity<Resource> download(String param) throws IOException {
// ...
Path path = Paths.get(file.getAbsolutePath());
ByteArrayResource resource = new ByteArrayResource(Files.readAllBytes(path));
return ResponseEntity.ok()
.headers(headers)
.contentLength(file.length())
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(resource);
}
Downloading a file from spring controllers
@RequestMapping(value = "/files/{file_name}", method = RequestMethod.GET)
public void getFile(
@PathVariable("file_name") String fileName,
HttpServletResponse response) {
try {
// get your file as InputStream
InputStream is = ...;
// copy it to response's OutputStream
org.apache.commons.io.IOUtils.copy(is, response.getOutputStream());
response.flushBuffer();
} catch (IOException ex) {
log.info("Error writing file to output stream. Filename was '{}'", fileName, ex);
throw new RuntimeException("IOError writing file to output stream");
}
}
Generally speaking, when you have response.getOutputStream()
, you can write anything there. You can pass this output stream as a place to put generated PDF to your generator. Also, if you know what file type you are sending, you can set
response.setContentType("application/pdf");
Related Topics
Why Have One Jvm Per Application
Java Conventions: Use Getters/Setters Within the Class
How to Cast an Object to an Int
Extract Source Code from .Jar File
The Most Sophisticated Way for Creating Comma-Separated Strings from a Collection/Array/List
Spring - Injecting a Dependency into a Servletcontextlistener
Tomcat - Maxthreads VS Maxconnections
Create Whole Path Automatically When Writing to a New File
Console Application with Java and Gradle
How Does the Jvm Ensure That System.Identityhashcode() Will Never Change
Remove Last Character of a Stringbuilder
How to Compile Java to Native Code
How to Configure Log4J with a Properties File