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")
Related
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?
I'm developing a Rest API, will be responsible to return a csv file as response.
This is my Api interface:
#Api(value = Constantes.REPORTS)
public interface ExtractFileApi {
#RequestMapping(value = Constantes.REPORTS_URL, produces = "application/csv", method = RequestMethod.GET)
public ResponseEntity<InputStreamResource> getExtractFile() throws IOException;
}
And this is my interface implementation:
#RestController
public class ExtractFileApiController implements ExtractFileApi {
#Override
public ResponseEntity<InputStreamResource> getExtractFile() throws IOException {
ClassPathResource pdfFile = new ClassPathResource("pdf-sample.csv");
HttpHeaders headers = new HttpHeaders();
headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
headers.add("Pragma", "no-cache");
headers.add("Expires", "0");
return ResponseEntity.ok().headers(headers).contentLength(pdfFile.contentLength())
.contentType(MediaType.parseMediaType("application/octet-stream"))
.body(new InputStreamResource(pdfFile.getInputStream()));
}
For now, my API return a link to download the file, but I don't know how to force the response to be exactly a CSV file (file.csv).
Can any one help me ?
You need to change return type to void and then use following code at end
Path path = Paths.get(pdfFile.getPath());
response.setHeader("content-disposition", "attachment; filename="
+ path.getFileName().toString().replace(" ", "_"));
try {
response.setContentType(Files.probeContentType(path));
response.setContentLength((int) Files.size(path));
// Copy bytes from source to destination, closes both streams.
FileCopyUtils.copy(Files.newInputStream(path),
response.getOutputStream());
} catch (IOException e) {
LOGGER.error("fetching file failed", e);
response.setStatus(500);
}
Try changing the production output MIME type to text/csv instead.
I am new to REST.I have to come up with Rest Controller that will take file name as parameter and display its contents.Below is the code
#RequestMapping(value = "/paths", method = RequestMethod.GET, produces = "application/pdf")
public ResponseEntity<InputStreamResource> downloadPDFFile() throws IOException {
ClassPathResource pdfFile = new ClassPathResource("PACKING LIST PDF 3000730933.pdf");
HttpHeaders headers = new HttpHeaders();
headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
headers.add("Pragma", "no-cache");
headers.add("Expires", "0");
return ResponseEntity.ok().headers(headers).contentLength(pdfFile.contentLength())
.body(new InputStreamResource(pdfFile.getInputStream()));
}
Above code will display the contents of pdf since produces = "application/pdf" is mentioned. I want to have generic code that will take all file formats like image,doc,docx,xls,xlsx and display the contents and send the contents in Response.
Can anyone please suggest me the approach for this ?
The best way to achieve this to have separate mapping (method), which will produce specific format for you.
#RequestMapping(value = "/paths")
public class YourApplication
{
#RequestMapping(value = "/pdf", method = RequestMethod.GET, produces = "application/pdf")
public ResponseEntity<InputStreamResource> downloadPDFFile() throws IOException {
// you code for pdf
}
#RequestMapping(value = "/xlsx", method = RequestMethod.GET, produces = "application/xlsx")
public ResponseEntity<InputStreamResource> downloadXLSXFile() throws IOException {
// you code for xlsx
}
}
Then, you can have following request url for specific type
/paths/pdf
and
/paths/xlsx
I'm trying to create a java REST service that will download a word doc. The file downloads but the contents are just garbage hex, not the actual Word doc contents. My sample code is below. What am I missing? The before & after files have the same amount of bytes.
#SuppressWarnings("resource")
#RequestMapping(value = "get/testdoc", method=RequestMethod.GET, produces="application/octet-stream)
public #ResponseBody ResponseEntity<byte[]> getTestDoc() throws Throwable{
File doc = new File("C:\\temp\\file.doc");
InputStream is = new FileInputStream(doc);
byte[] bytes = IOUtils.toByteArray(is);
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);
responseHeaders.set("Content-Disposition" , "Attachment; filename=file.doc");
responseHeaders.setContentLength(ProposalDoc.length());
return new ResponseEntity<byte[]>(bytes, responseHeaders, HttpStatus.OK);
}
I think there are two problems:
1. The Length Header:
I my opinion there is at least one very strange line:
responseHeaders.setContentLength(ProposalDoc.length());
I think, it should be:
responseHeaders.setContentLength(bytes.length);
2. #ResponseBody Annotation
If you use return type ResponseEntity<byte[]>, then you must NOT add #ResponseBody.
#RequestMapping(value = "get/testdoc", method=RequestMethod.GET)
public ResponseEntity<byte[]> getTestDoc() throws Throwable{
...
}
try to replace produces="application/octet-stream")
with produces="application/vnd.ms-word")
Thanks for all the help. I ended up bypassing Spring & attaching the file to the response, as listed in the code below. I'm suspecting that sprint was converting the bytes somehow behind the scenes. I looked into configuring the ByteArrayHttpMessageConverter, but that didn't seem to help. This is good enough for me, for now.
#SuppressWarnings("resource")
#RequestMapping(value = "get/doc", method=RequestMethod.GET, produces="application/octet-stream")
public HttpEntity getProposalDocs(HttpServletResponse response) throws Throwable{
File doc = new File("C:\\temp\\file.doc");
InputStream is = new FileInputStream(doc);
response.setHeader("Content-Disposition", "attachment;filename=\"test.doc\"");
response.setHeader("Content-Type", "application/octet-stream;");
StreamUtils.copy(is ,response.getOutputStream());
return new ResponseEntity(HttpStatus.OK);
}
Check this code also, it works fine with me.
#RequestMapping(value = "/get/doc" , method = RequestMethod.GET ,
produces = "application/msword")
public ResponseEntity<InputStreamResource> getProposalDocs() throws IOException{
ClassPathResource docfile = new ClassPathResource("file.doc");
HttpHeaders headers
= new HttpHeaders();
headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
headers.add("Pragma", "no-cache");
headers.add("Expires", "0");
return ResponseEntity.ok()
.headers(headers)
.contentLength(docfile.contentLength())
.contentType(MediaType.parseMediaType("application/msword"))
.body(new InputStreamResource(docfile.getInputStream()));
}
EDITED: the idea that worked with me to return InputStreamResource instead of byte[].
Also specify the content type as produces="application/octet-stream".
This works fine with me without needed to bypass servlet response..
I am getting this error:
the request was rejected because no multipart boundary was found
when I upload an image through postman like this: I have saveImage as url, in header I have content/type and multipart/form-data and choossed raw and uploaded an image.
This is my controller:
#RequestMapping(value = "/saveImage",
method = RequestMethod.POST,
consumes = "application/json", produces = "application/json;charset=UTF-8")
public #ResponseBody ResponseEntity<GenericResponseVO<? extends IServiceVO>> getImage(
#RequestBody(required = false) GenericRequestVO<ImageCriteriaVO> imageCriteriaVO,HttpServletRequest request, HttpServletResponse response ) {
return requestHandlerInvoker.callHandler(
HandlerName.SAVE_IMAGE_HANDLER,request,response,imageCriteriaVO);
}
This my handler:
public GenericResponseVO<? extends IServiceVO> handleRequest(
UserContext userCtx, HttpServletRequest httpRequest,
HttpServletResponse httpResponse,
GenericRequestVO<? extends IServiceVO> requestVO)
throws O2Exception {
GenericResponseVO<ImageResultVO> imageVO = new GenericResponseVO<ImageResultVO>();
ImageResultVO imageResultVO = (ImageResultVO) serviceInvoker
.callService(ServiceName.IMAGE_SERVICE, userCtx,requestVO.getBody());
imageVO.setBody(imageResultVO);
imageVO.getHeader().setStatus(new Status(Status.SUCCESS, "Token"));
return imageVO;
This my service:
public IServiceVO service(UserContext userCtx, IServiceVO inputVO)
throws O2Exception {
LOG.info(LoggingEvent.Image_INPROGRESS,"Inside get list of treasure hunt for corporate service");
ImageCriteriaVO imageCriteriaVO = (ImageCriteriaVO) inputVO;
System.out.println(imageCriteriaVO.getFile());
ImageResultVO imageResultVO = new ImageResultVO();
CommonsMultipartFile file = null;
String fileName = null;
if (!file.isEmpty()) {
try {
fileName = file.getOriginalFilename();
byte[] bytes = file.getBytes();
BufferedOutputStream buffStream =
new BufferedOutputStream(new FileOutputStream(new File("F:/" + fileName)));
buffStream.write(bytes);
buffStream.close();
}catch (Exception e) {
}
}
return imageResultVO;
}
This my criteria class:
public class ImageCriteriaVO implements IServiceVO{
private byte[] file;
public byte[] getFile() {
return file;
}
public void setFile(byte[] file) {
this.file = file;
}
}
I hope this answer may help
Remove Content-Type from header when you are trying to upload, Postman will do it automatically.
if you set Content-Type: undefined, Postman will automatically sets Content-Type to multipart/form-data and depending upon the media it will set boundary.
Mail user agents ignore content-disposition headers in the messages.
File name should be mentioned in filename parameter. Otherwise should be mentioned in both filename and the name parameters5
multipart/form-data with Angular JS