download a file from Spring boot rest service
I am trying to download a file from a Spring boot rest service.
@RequestMapping(path="/downloadFile",method=RequestMethod.GET)
@Consumes(MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<InputStreamReader> downloadDocument(
String acquistionId,
String fileType,
Integer expressVfId) throws IOException {
File file2Upload = new File("C:\\Users\\admin\\Desktop\\bkp\\1.rtf");
HttpHeaders headers = new HttpHeaders();
headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
headers.add("Pragma", "no-cache");
headers.add("Expires", "0");
InputStreamReader i = new InputStreamReader(new FileInputStream(file2Upload));
System.out.println("The length of the file is : "+file2Upload.length());
return ResponseEntity.ok().headers(headers).contentLength(file2Upload.length())
.contentType(MediaType.parseMediaType("application/octet-stream"))
.body(i);
}
When I tried to download the file from the browser, it starts the download, but always fails. Is there anything wrong with the service which is causing the download to fail?
Solution 1:
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);
}
Solution 2:
The below Sample code worked for me and might help someone.
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
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 java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@RestController
@RequestMapping("/app")
public class ImageResource {
private static final String EXTENSION = ".jpg";
private static final String SERVER_LOCATION = "/server/images";
@RequestMapping(path = "/download", method = RequestMethod.GET)
public ResponseEntity<Resource> download(@RequestParam("image") String image) throws IOException {
File file = new File(SERVER_LOCATION + File.separator + image + EXTENSION);
HttpHeaders header = new HttpHeaders();
header.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=img.jpg");
header.add("Cache-Control", "no-cache, no-store, must-revalidate");
header.add("Pragma", "no-cache");
header.add("Expires", "0");
Path path = Paths.get(file.getAbsolutePath());
ByteArrayResource resource = new ByteArrayResource(Files.readAllBytes(path));
return ResponseEntity.ok()
.headers(header)
.contentLength(file.length())
.contentType(MediaType.parseMediaType("application/octet-stream"))
.body(resource);
}
}
Solution 3:
I would suggest using a StreamingResponseBody since with it, the application can write directly to the response (OutputStream), without holding up the Servlet container thread. It is a good approach if you are downloading a very large file.
@GetMapping("download")
public StreamingResponseBody downloadFile(HttpServletResponse response, @PathVariable Long fileId) {
FileInfo fileInfo = fileService.findFileInfo(fileId);
response.setContentType(fileInfo.getContentType());
response.setHeader(
HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=\"" + fileInfo.getFilename() + "\"");
return outputStream -> {
int bytesRead;
byte[] buffer = new byte[BUFFER_SIZE];
InputStream inputStream = fileInfo.getInputStream();
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
};
}
Ps.: When using StreamingResponseBody, it is highly recommended to configure TaskExecutor used in Spring MVC for executing asynchronous requests. TaskExecutor is an interface that abstracts the execution of a Runnable.
More info: https://medium.com/swlh/streaming-data-with-spring-boot-restful-web-service-87522511c071