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?

7 Answers

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); } 
7

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); } } 
1

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:

3

I want to share a simple approach for downloading files with JavaScript (ES6), React and a Spring Boot backend:

  1. Spring boot Rest Controller

Resource from org.springframework.core.io.Resource

 @SneakyThrows @GetMapping("/files/{filename:.+}/{extraVariable}") @ResponseBody public ResponseEntity<Resource> serveFile(@PathVariable String filename, @PathVariable String extraVariable) { Resource file = storageService.loadAsResource(filename, extraVariable); return ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + file.getFilename() + "\"") .body(file); } 
  1. React, API call using AXIOS

Set the responseType to arraybuffer to specify the type of data contained in the response.

export const DownloadFile = (filename, extraVariable) => { let url = ' + filename + '/' + extraVariable; return axios.get(url, { responseType: 'arraybuffer' }).then((response) => { return response; })}; 

Final step > downloading
with the help of js-file-download you can trigger browser to save data to file as if it was downloaded.

DownloadFile('filename.extension', 'extraVariable').then( (response) => { fileDownload(response.data, filename); } , (error) => { // ERROR }); 
1

If you need to download a huge file from the server's file system, then ByteArrayResource can take all Java heap space. In that case, you can use FileSystemResource

2
 @GetMapping("/downloadfile/{productId}/{fileName}") public ResponseEntity<Resource> downloadFile(@PathVariable(value = "productId") String productId, @PathVariable String fileName, HttpServletRequest request) { // Load file as Resource Resource resource; String fileBasePath = "C:\\Users\\v_fzhang\\mobileid\\src\\main\\resources\\data\\Filesdown\\" + productId + "\\"; Path path = Paths.get(fileBasePath + fileName); try { resource = new UrlResource(path.toUri()); } catch (MalformedURLException e) { e.printStackTrace(); return null; } // Try to determine file's content type String contentType = null; try { contentType = request.getServletContext().getMimeType(resource.getFile().getAbsolutePath()); } catch (IOException ex) { System.out.println("Could not determine file type."); } // Fallback to the default content type if type could not be determined if (contentType == null) { contentType = "application/octet-stream"; } return ResponseEntity.ok().contentType(MediaType.parseMediaType(contentType)) .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"") .body(resource); } 

To test it, use postman

using Apache IO could be another option for copy the Stream

@RequestMapping(path = "/file/{fileId}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity<?> downloadFile(@PathVariable(value="fileId") String fileId,HttpServletResponse response) throws Exception { InputStream yourInputStream = ... IOUtils.copy(yourInputStream, response.getOutputStream()); response.flushBuffer(); return ResponseEntity.ok().build(); } 

maven dependency

 <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-io</artifactId> <version>1.3.2</version> </dependency> 
1