Careeroza
Downloading Files
Serving a Stored File
@GetMapping("/api/files/products/{productId}/{name}")
public ResponseEntity<Resource> image(@PathVariable Long productId, @PathVariable String name) throws IOException {
Path dir = uploadDir.resolve("products").resolve(productId.toString()).normalize();
Path file = dir.resolve(name).normalize();
if (!file.startsWith(dir) || !Files.exists(file)) {
return ResponseEntity.notFound().build();
}
String type = Files.probeContentType(file);
return ResponseEntity.ok()
.contentType(MediaType.parseMediaType(type != null ? type : "application/octet-stream"))
.cacheControl(CacheControl.maxAge(Duration.ofDays(30)).cachePublic())
.body(new FileSystemResource(file));
}Generated Downloads (Invoices, CSV)
@GetMapping("/api/orders/{id}/invoice")
public ResponseEntity<byte[]> invoice(@PathVariable Long id) {
byte[] pdf = invoiceService.renderPdf(id);
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION,
ContentDisposition.attachment().filename("INV-" + id + ".pdf").build().toString())
.contentType(MediaType.APPLICATION_PDF)
.body(pdf);
}
@GetMapping(value = "/api/admin/orders/export", produces = "text/csv")
public ResponseEntity<StreamingResponseBody> exportCsv() {
StreamingResponseBody body = out -> {
try (var writer = new PrintWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8))) {
writer.println("orderId,userId,total,status");
orderService.streamAll(o -> writer.printf("%d,%d,%s,%s%n", o.id(), o.userId(), o.total(), o.status()));
}
};
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"orders.csv\"")
.body(body);
}Example
curl -OJ localhost:8080/api/orders/1001/invoiceExample output
curl: Saved to filename 'INV-1001.pdf'
| Header | Meaning |
|---|---|
Content-Disposition: inline | show in the browser (images, PDFs preview) |
Content-Disposition: attachment; filename="x" | force a download with that name |
Content-Type | what the bytes are (application/pdf, text/csv) |
Practice
Exercise: Return a product catalogue as a downloadable CSV named products.csv from GET /api/products/export, using a byte[] body.
Show answer
@GetMapping("/api/products/export")
public ResponseEntity<byte[]> export() {
StringBuilder sb = new StringBuilder("id,name,price,stock").append(System.lineSeparator());
productService.findAll().forEach(p ->
sb.append(p.id()).append(',').append(p.name()).append(',')
.append(p.price()).append(',').append(p.stock()).append(System.lineSeparator()));
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"products.csv\"")
.contentType(MediaType.parseMediaType("text/csv"))
.body(sb.toString().getBytes(StandardCharsets.UTF_8));
}