I'm using SpringBoot 3.0.1 and I'm trying to get a file stored in the backend using Axios.
The controller is the following:
#GetMapping(value = "/api/files/{fileName}", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public ResponseEntity<?> getFile(final #PathVariable("fileName") String fileName) {
try {
Path filePath = Path.of(fileName);
File file = filePath.toFile();
HttpHeaders responseHeaders = new HttpHeaders();
String filename = filePath.getFileName().toString();
responseHeaders
.setContentDisposition(ContentDisposition.builder("attachment")
.filename(filename, StandardCharsets.UTF_8)
.build());
FileSystemResource fileSystemResource = new FileSystemResource(file);
return ResponseEntity
.ok()
.headers(responseHeaders)
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.contentLength(file.length())
.lastModified(file.lastModified())
.body(fileSystemResource);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
When I get the answer (status is 200), the header I've set in the controller is not given. In particular, the Content-Disposition header is not defined in the answer.
I'm wondering if there is any missing configuration that must be set in Sprint Boot in order to be allowed to set a custom header. Anyone who knows what can cause this and how to fix it?
Related
I have a controller and an Exporter class to create pdf of one class data in Spring boot. It works at localhost. And I can send emails with attachments which are in the resources/static/ directory from this link:
https://asbnotebook.com/2020/01/26/send-email-with-attachment-spring-boot/
I want to email the pdf file created at fly. I tried to combine them but it didnt work.
public String sendMail(EmailRequestDto request, Map<String, String> model) {
String response;
MimeMessage message = mailSender.createMimeMessage();
try {
MimeMessageHelper helper = new MimeMessageHelper(message, MimeMessageHelper.MULTIPART_MODE_MIXED_RELATED,
StandardCharsets.UTF_8.name());
Template template = configuration.getTemplate("email.ftl");
String html = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
helper.setTo(request.getTo());
helper.setFrom(request.getFrom());
helper.setSubject(request.getSubject());
helper.setText(html, true);
List<PurchaseDetail> cities = (List<PurchaseDetail>)basketService.getPurchases();
ByteArrayInputStream bis = InvoicePdfExporter.citiesReport(cities);
HttpHeaders headers = new HttpHeaders(); headers.add("ContentDisposition",
"inline; filename=citiesreport.pdf");
InputStreamResource rs= (new InputStreamResource(bis)) ;
//this gives error because rs is a inputStreamResource but not InputStream
helper.addAttachment("citiesreport.pdf",newByteArrayResource(IOUtils.toByteArray(rs)));
mailSender.send(message);
response = "Email has been sent to :" + request.getTo();
} catch (MessagingException | IOException | TemplateException e) {
response = "Email send failure to :" + request.getTo();
}
return response;
}
And my working controller class which creates pdf. InvoicePdfExporter class adds datatable to document and returns as return new ByteArrayInputStream(out.toByteArray());:
#RequestMapping(value = "/pdfreport", method = RequestMethod.GET,
produces = MediaType.APPLICATION_PDF_VALUE)
public ResponseEntity<InputStreamResource> citiesReport() throws IOException
{
List<PurchaseDetail> purchases = (List<PurchaseDetail>)
basketService.getPurchases();
ByteArrayInputStream bis = InvoicePdfExporter.citiesReport(purchases);
HttpHeaders headers = new HttpHeaders(); headers.add("Content-Disposition",
"inline; filename=citiesreport.pdf");
return
ResponseEntity.ok().headers(headers).contentType(MediaType.APPLICATION_PDF)
.body(new InputStreamResource(bis)) ; }
}
I really need help I really dont understand from IOStreams, I tried many things but none of them solved my problem. Thanks!!
Edit:
I solved this problem by changing the return type of my InvoicePdfExporter to InputStreamSource and changed to this:
List<PurchaseDetail> cities = (List<PurchaseDetail>)basketService.getPurchases();
InputStreamSource bis =InvoicePdfExporter.citiesReport(cities);
HttpHeaders headers = new HttpHeaders(); headers.add("Content-Disposition",
"inline; filename=citiesreport.pdf");
helper.addAttachment("citiesreport.pdf",bis, "application/pdf" );
I'm using this code to download a image from angular app.
#RequestMapping("/files/{merchant_id}")
public ResponseEntity<byte[]> downloadLogo(#PathVariable("merchant_id") Integer merchant_id) throws IOException {
File file = new File(UPLOADED_FOLDER, merchant_id.toString() + "/merchant_logo.png");
InputStream in = FileUtils.openInputStream(file);
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.IMAGE_PNG);
return new ResponseEntity<byte[]>(IOUtils.toByteArray(in), headers, HttpStatus.CREATED);
}
But when I try to download a image which is not found I get NPE which is normal. How I can return empty response when the image file is not found? Something like:
return ResponseEntity.ok(...).orElse(file.builder().build()));
Can you give me some advice how to fix this?
Just choose a ResponseEntity constructor that is without body argument to create ResponseEntity
File file = new File(UPLOADED_FOLDER, merchant_id.toString() + "/merchant_logo.png");
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.IMAGE_PNG);
if (!file.exists()) {
return new ResponseEntity<byte[]>(headers,HttpStatus.NOT_FOUND);
}else{
InputStream in = FileUtils.openInputStream(file);
return new ResponseEntity<byte[]>(IOUtils.toByteArray(in), headers, HttpStatus.OK);
}
I change it to return 404 status code when the images does not exist and 200 when the images exist which better align with HTTP status code 's semantic meaning.
I could see many related topics, but I have a specific problem. I am using spring boot controller to download a zip file. I am able to download the file when it is http verb get, but as I have to pass a big json payload I changed to post. Since then instead of downloading it as file it is responding the contents of the file with some ascii characters. Below is the method in controller for downloading the file.
#ApiResponses(value = { #ApiResponse(code = 404, message = "file could not be found"),
#ApiResponse(code = 200, message = "File was created sucessfully") })
#PostMapping(path="/download-file/1.0", produces="application/zip")
public ResponseEntity<InputStreamResource> downloadFile(
#ApiParam(value = "File creation contents", required = true) #RequestBody InputDetailsVO inputDetailsVO) {
File file = null;
InputStreamResource resource = null;
HttpHeaders headers = new HttpHeaders();
try {
//Creating InputStreamResource out of zip file
resource = new InputStreamResource(new FileInputStream(file));
String contentType = "application/zip";
if (!StringUtils.isEmpty(contentType)) {
headers.setContentType(MediaType.parseMediaType(contentType));
}
headers.add("Content-Disposition","attachment; filename=\""+file.getName()+"\"");
} catch (Exception e) {
log.error("Issue with file creation",e);
}
return ResponseEntity.ok()
.contentLength(file.length())
.contentType(MediaType
.parseMediaType(MediaType.APPLICATION_OCTET_STREAM_VALUE))
.headers(headers).body(resource);
}
Below is the response I am getting instead of file download
PK;��N <?xml version="1.0" encoding="UTF-8"?>
<employeeDetails>
<name>Harry</name>
<age>30</30>
<email>test#test.com</test>
</employeeDetails>PK�qB�#Y;YPK;��N�qB�#Y;Yemployee details.xmlPKL�Y
Try like this, you can download any type of file. I assume that InputDetailsVO contains the name of the file or you can have your own logic to pick the file name. On the top of this method, you can provide swagger related annotations.
#PostMapping(value = "/download-file/1.0", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public ResponseEntity<?> downloadFile(#RequestBody InputDetailsVO inputDetailsVO) {
String dirPath = "your-location-path";
byte[] fileBytes = null;
try {
String fileName = inputDetailsVO.getFileName();
fileBytes = Files.readAllBytes(Paths.get(dirPath + fileName));
} catch (IOException e) {
e.printStackTrace();
}
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileName + "\"")
.body(fileBytes);
}
I also had a similar use case. I am sharing the code which had solved the issue.
#RequestMapping(value="/download",method=RequestMethod.GET,produces="application/zip" )
public ResponseEntity<?> download(HttpServletResponse response) throws IOException
{
//Some Code...
File file = new File("F:\\Folder\\Folder\\Folder\\"+filename);
InputStreamResource resource2 = new InputStreamResource(new FileInputStream(file));
response.setContentType("application/zip");
response.setHeader("Content-Disposition", String.format("inline; filename=\"" + filename + "\""));
response.setHeader("responseType", "arraybuffer");
response.setHeader("Content-Length", ""+file.length());
return new ResponseEntity<InputStreamResource>(resource2,HttpStatus.ACCEPTED);
}
I'm creating spring boot application that send a file in body response, to this i use this code :
FileSystemResource pdfFile = new FileSystemResource(outputFile);
return ResponseEntity
.ok()
.contentLength(pdfFile.contentLength())
.contentType(MediaType.parseMediaType("application/pdf"))
.body(new ByteArrayResource(IOUtils.toByteArray(pdfFile.getInputStream())));
I'm wondering if there's any alternative way for send file other than using FileSystemResource ?
Please, If there's any suggestion, do not hesitate.
Thank You !
This is a simplified version of how I usually do it, but it does pretty much the same thing:
#RequestMapping(method = RequestMethod.GET, value = "/{id}")
public ResponseEntity<byte[]> getPdf(#PathVariable Long id) throws IOException {
final String filePath = pdfFilePathFinder.find(id);
final byte[] pdfBytes = Files.readAllBytes(Paths.get(filePath));
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.parseMediaType("application/pdf"));
headers.setContentDispositionFormData("attachment", null);
headers.setCacheControl("no-cache");
return new ResponseEntity<>(pdfBytes, headers, HttpStatus.OK);
}
I followed this tutorial to implement rest API with Spring Boot for downloading files (xml format).
My controller class is as follows:
#RestController
public class RistoreController {
#Autowired
private RistoreService ristoreService;
#RequestMapping(
value = "/ristore/foundation/{trf}",
method = RequestMethod.GET,
produces = "application/xml")
public ResponseEntity<InputStream> getXMLById(#PathVariable("trf") String trf) throws IOException {
InputStream inputStream = ristoreService.findByTRF(trf);
return ResponseEntity
.ok()
.contentType(MediaType.parseMediaType("application/octet-stream"))
.body(inputStream);
}
}
I have service interface RistoreService autowired in the controller and Bean class for that service looks like this:
#Service
public class RistoreServiceBean implements RistoreService {
public InputStream findByTRF(String trf) throws IOException {
String filePath = "/Users/djiao/Box Sync/Work/Projects/RIStore/foundation/foundation_new/" + trf + ".xml";
File file = new File(filePath);
return new FileInputStream(file);
}
}
I tested the application using the following curl command:
curl -i -H "Accept: application/xml" http://localhost:8080/ristore/foundation/TRF133672_1455294493597
However, I got 406 error, "Not Acceptable". Something wrong with the file format?
Try to change the definition of the controller that way
#RequestMapping(value = "/ristore/foundation/{trf}", method = RequestMethod.GET, produces = "application/xml")
public ResponseEntity<InputStreamResource> downloadXMLFile(#PathVariable("trf") String trf)
throws IOException {
// Optinal headers configuration
HttpHeaders headers = new HttpHeaders();
headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
headers.add("Pragma", "no-cache");
headers.add("Expires", "0");
// get the inputStream
InputStream xmlFileInputStream = ristoreService.findByTRF(trf);
return ResponseEntity
.ok()
.headers(headers)
.contentType(MediaType.parseMediaType("application/octet-stream"))
.body(new InputStreamResource(xmlFileInputStream));
}
Then your service class would be :
#Service
public class RistoreServiceBean implements RistoreService {
public InputStream findByTRF(String trf) throws IOException {
String filePath = "/Users/djiao/Box Sync/Work/Projects/RIStore/foundation/foundation_new/" + trf + ".xml";
File file = new File(filePath);
return new FileInputStream(file);
}
}
406 Not Acceptable
The resource identified by the request is only capable of generating response entities which have content characteristics not acceptable according to the accept headers sent in the request.
That means that the inputstream you return must be considered as a resource as soon as you have a REST controller.
The following two lines in your code contradict each other:
.contentType(MediaType.parseMediaType("application/octet-stream"))
and
produces = "application/xml")