Explain Codes LogoExplain Codes Logo

Downloading a file from Spring controllers

java
spring
file-downloading
rest-api
Nikita BarsukovbyNikita Barsukov·Oct 5, 2024
TLDR

For swift file downloads, use Spring's ResponseEntity in your controller. Deliver a ResponseEntity<Resource> with the Content-Disposition header set to "attachment". Use MediaType to specify the MIME type, ensuring the browser treats the file appropriately.

@GetMapping("/download") public ResponseEntity<Resource> downloadFile() { Resource file = new FileSystemResource("/path/to/myfile.txt"); return ResponseEntity.ok() .contentType(MediaType.APPLICATION_OCTET_STREAM) .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + file.getFilename() + "\"") .body(file); }

Replace "/path/to/myfile.txt" with your specific file path to make this code snippet ready for use. This sends your file as a downloadable link, named by its original name on the server.

Broadening the Horizon

Serving specific file paths

When working with fluctuating file paths, @PathVariable comes to the rescue:

@GetMapping("/download/{filename:.+}") public ResponseEntity<Resource> downloadFile(@PathVariable String filename) { Resource file = new FileSystemResource("uploads/" + filename); // Here be dragons 🐉! Make sure the path is safe! return ResponseEntity.ok() .contentType(MediaType.parseMediaType("application/octet-stream")) .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + file.getFilename() + "\"") .body(file); }

Handling Errors like a Boss

Graceful error handling is essential, use @ControllerAdvice for sweeping handling or try-catch blocks for localized issues:

@GetMapping("/download/{filename:.+}") public ResponseEntity<Resource> downloadFile(@PathVariable String filename, HttpServletResponse response) { try { Resource file = new FileSystemResource("uploads/" + filename); // Performance art: file juggling 🤹‍♀️, and check to see if it landed! return ResponseEntity.ok() .contentType(MediaType.APPLICATION_OCTET_STREAM) .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + file.getFilename() + "\"") .body(file); } catch (Exception e) { // Whoops! Error alert 🚨. Log and retreat. response.setStatus(HttpServletResponse.SC_NOT_FOUND); return ResponseEntity.status(HttpStatus.NOT_FOUND).build(); } }

Storing Large Files in the Cloud

When streaming huge files, don't bog down memory. Instead, float on cloud 9 with StreamResource and ResourceHttpMessageConverter:

@GetMapping("/download/{filename:.+}") public ResponseEntity<Resource> downloadFileStream(@PathVariable String filename) throws IOException { Path path = Paths.get("uploads/" + filename); InputStreamResource resource = new InputStreamResource(Files.newInputStream(path)); // Remember, it's not the size that matters, it's how you use it 😉 return ResponseEntity.ok() .contentLength(Files.size(path)) .contentType(MediaType.APPLICATION_OCTET_STREAM) .body(resource); }

Streamlining with Apache Commons IO

Apache Commons IO to the rescue for an elegant way to shuttle bytes between streams:

@GetMapping("/download/{filename:.+}") public void downloadWithCommonsIO(@PathVariable String filename, HttpServletResponse response) throws IOException { File file = new File("uploads/" + filename); // so fresh and so clean 🛀 response.setContentType("application/octet-stream"); response.setHeader("Content-Disposition", "attachment; filename=\"" + file.getName() + "\""); try (InputStream is = new FileInputStream(file); OutputStream os = response.getOutputStream()) { IOUtils.copy(is, os); // copy-paste like a pro 😎 response.flushBuffer(); } // Watch out for those IOExceptions! They bite 🦈 }

Advanced Solutions

Custom PDF Generation

To serve PDF files, iText might be your knight in shining armor:

@GetMapping("/download/pdf") public ResponseEntity<InputStreamResource> downloadPdf() throws IOException { ByteArrayOutputStream outputStream = createPdf(); // Masterpiece generator here 🖼️ ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray()); HttpHeaders headers = new HttpHeaders(); headers.add("Content-Disposition", "inline; filename=myfile.pdf"); return ResponseEntity .ok() .headers(headers) .contentType(MediaType.APPLICATION_PDF) .body(new InputStreamResource(inputStream)); }

Auto-detect mime type

Leverage Spring's superpower to auto-detect mime type:

// It's a bird 🦅, it's a plane ✈️, no it's mime type detection! @GetMapping("/download/{filename:.+}") public ResponseEntity<Resource> downloadFileAutoMimeType(@PathVariable String filename) throws IOException { Resource file = new FileSystemResource("uploads/" + filename); String mimeType = ServletUriComponentsBuilder.fromCurrentContextPath() .build() .getServletContext() .getMimeType(file.getFile().getAbsolutePath()); return ResponseEntity.ok() .contentType(MediaType.parseMediaType(mimeType)) // apply mime type like a boss 😎 .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + file.getFilename() + "\"") .body(file); }

Template-based file generation

For the elegant solution of template-based PDFs, Freemarker synergizes well with iText:

@GetMapping("/download/templatePdf") // Keep your design 👗 to your PDFs. public ResponseEntity<InputStreamResource> downloadTemplatePdf(@RequestParam String orderId) throws IOException { ByteArrayInputStream byteArrayInputStream = pdfService.generatePdfFromTemplate(orderId); // Craftsmanship included HttpHeaders headers = new HttpHeaders(); headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=order-" + orderId + ".pdf"); return ResponseEntity .ok() .headers(headers) .contentType(MediaType.APPLICATION_PDF) .body(new InputStreamResource(byteArrayInputStream)); }