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);
}
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 try to return the byte[] object by using ResponseEntity
return new ResponseEntity<>(new Response(ResultCode.SUCCESS, SUCCESS).setResult(dataObj), HttpStatus.OK);
but and got a response like that:
and another way I try to return the direct byte[] object
return dataObj;
the response like that:
Why do we have different? and Can I return the ResponseEntity with data like the 2nd image?
Try following code. It will send image itself in response on accessing this API.
#GetMapping(value = "/image/{id}")
public ResponseEntity<?> getImageById(#PathParam Long id) throws Exception {
String imageData = this.entityRepository.findById(id).orElse(null).getData();
byte[] imageByte = Base64.getDecoder().decode(new String(imageData).getBytes("UTF-8"));
MediaType mediaType = MediaType.APPLICATION_OCTET_STREAM;
HttpHeaders headers = new HttpHeaders();
headers.setContentType(mediaType);
return new ResponseEntity<>(imageByte, headers, HttpStatus.OK);
}
I encountered a problem with downloading grid fs stored image via spring controller. When trying to open the downloaded file the image viewer says that it is corrupted, it turns out that the image is in base64 format.
There is the controller part:
#Override
#RequestMapping(value = "/image_download", method = RequestMethod.GET)
public ResponseEntity<byte[]> downloadImage(...) throws IOException {
final GridFSDBFile image = getImageFromGrifFs(...);
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.valueOf(image.getContentType()));
headers.setContentDispositionFormData("attachment", image.getFileName());
final byte[] content = IOUtils.toByteArray(image.getInputStream());
return new ResponseEntity<>(content, headers, HttpStatus.OK);
}
Spring version is 4.3.11.
And here are the message converters:
#Override
public void configureMessageConverters(final List<HttpMessageConverter<?>> converters) {
converters.add(new MappingJackson2HttpMessageConverter());
converters.add(byteArrayHttpMessageConverter());
super.configureMessageConverters(converters);
}
#Bean
public ByteArrayHttpMessageConverter byteArrayHttpMessageConverter() {
final ByteArrayHttpMessageConverter arrayHttpMessageConverter = new ByteArrayHttpMessageConverter();
arrayHttpMessageConverter.setSupportedMediaTypes(getSupportedMediaTypes());
return arrayHttpMessageConverter;
}
private List<MediaType> getSupportedMediaTypes() {
final List<MediaType> list = new ArrayList<>();
list.add(MediaType.IMAGE_JPEG);
list.add(MediaType.IMAGE_PNG);
list.add(MediaType.IMAGE_GIF);
list.add(MediaType.APPLICATION_OCTET_STREAM);
return list;
}
I also tried using InputStreamResource the following way in the controller:
return ResponseEntity.ok()
.contentLength(image.getLength())
.contentType(MediaType.parseMediaType(image.getContentType()))
.body(new InputStreamResource(image.getInputStream()));
But got the exception:
Could not write content: No serializer found for class com.mongodb.gridfs.GridFSDBFile$MyInputStream
Any help appreciated. Thank you.
After I'd done some more digging I found good explanation: https://stackoverflow.com/a/44943494/2421204
And indeed adding (produces = "image/jpeg") to RequestMapping solved the issue.
#RequestMapping(value = "/image_download", method = RequestMethod.GET, produces = "image/jpeg")
The images that are downloaded are in binary.
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..
What is the most appropriate, and standard, way to set the Content-Disposition=attachment and filename=xyz.zip using Spring 3 FileSystemResource?
The action looks like :
#ResponseBody
#RequestMapping(value = "/action/{abcd}/{efgh}", method = RequestMethod.GET, produces = "application/zip")
#PreAuthorize("#authorizationService.authorizeMethod()")
public FileSystemResource doAction(#PathVariable String abcd, #PathVariable String efgh) {
File zipFile = service.getFile(abcd, efgh);
return new FileSystemResource(zipFile);
}
Although the file is a zip file so the browser always downloads the file, but I would like to explicitly mention the file as attachment, and also provide a filename that has nothing to do with the files actual name.
There might be workarounds for this problem, but I would like to know the proper Spring and FileSystemResource way to achieve this goal.
P.S. The file that is being used here is a temporary file, marked for deletion when the JVM exists.
In addition to the accepted answer, Spring has the class ContentDisposition specific for this purpose. I believe it deals with the file name sanitization.
ContentDisposition contentDisposition = ContentDisposition.builder("inline")
.filename("Filename")
.build();
HttpHeaders headers = new HttpHeaders();
headers.setContentDisposition(contentDisposition);
#RequestMapping(value = "/action/{abcd}/{efgh}", method = RequestMethod.GET)
#PreAuthorize("#authorizationService.authorizeMethod(#id)")
public HttpEntity<byte[]> doAction(#PathVariable ObjectType obj, #PathVariable Date date, HttpServletResponse response) throws IOException {
ZipFileType zipFile = service.getFile(obj1.getId(), date);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
response.setHeader("Content-Disposition", "attachment; filename=" + zipFile.getFileName());
return new HttpEntity<byte[]>(zipFile.getByteArray(), headers);
}
#RequestMapping(value = "/files/{file_name}", method = RequestMethod.GET)
#ResponseBody
public FileSystemResource getFile(#PathVariable("file_name") String fileName,HttpServletResponse response) {
response.setContentType("application/pdf");
response.setHeader("Content-Disposition", "attachment; filename=somefile.pdf");
return new FileSystemResource(new File("file full path"));
}
Here is an alternative approach for Spring 4. Note that this example clearly does not use good practices regarding filesystem access, this is just to demonstrate how some properties can be set declaratively.
#RequestMapping(value = "/{resourceIdentifier}", method = RequestMethod.GET, produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
// #ResponseBody // Needed for #Controller but not for #RestController.
public ResponseEntity<InputStreamResource> download(#PathVariable(name = "resourceIdentifier") final String filename) throws Exception
{
final String resourceName = filename + ".dat";
final File iFile = new File("/some/folder", resourceName);
final long resourceLength = iFile.length();
final long lastModified = iFile.lastModified();
final InputStream resource = new FileInputStream(iFile);
return ResponseEntity.ok()
.header("Content-Disposition", "attachment; filename=" + resourceName)
.contentLength(resourceLength)
.lastModified(lastModified)
.contentType(MediaType.APPLICATION_OCTET_STREAM_VALUE)
.body(resource);
}
Made few changes to both given answers and I ended up with the best of both in my project where I needed to extract an image from the database as a blob and then serve it to the clients :
#GetMapping("/images/{imageId:.+}")
#ResponseBody
public ResponseEntity<FileSystemResource> serveFile(#PathVariable #Valid String imageId,HttpServletResponse response)
{
ImageEntity singleImageInfo=db.storage.StorageService.getImage(imageId);
if(singleImageInfo==null)
{
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
}
Blob image=singleImageInfo.getImage();
try {
String filename= UsersExtra.GenerateSession()+"xxyy"+singleImageInfo.getImage1Ext().trim();
byte [] array = image.getBytes( 1, ( int ) image.length() );
File file = File.createTempFile(UsersExtra.GenerateSession()+"xxyy", singleImageInfo.getImage1Ext().trim(), new File("."));
FileOutputStream out = new FileOutputStream( file );
out.write( array );
out.close();
FileSystemResource testing=new FileSystemResource(file);
String mimeType = "image/"+singleImageInfo.getImage1Ext().trim().toLowerCase().replace(".", "");
response.setContentType(mimeType);
String headerKey = "Content-Disposition";
String headerValue = String.format("attachment; filename=\"%s\"", filename);
response.setHeader(headerKey, headerValue);
// return new FileSystemResource(file);
return ResponseEntity.status(HttpStatus.OK).body( new FileSystemResource(file));
}catch(Exception e)
{
System.out.println(e.getMessage());
}
return null;
}
Using a ResponseEntity in Kumar's code will help you respond with the correct Response code.
Note: converting from a blob to a file is quoted from this link:
Snippet to create a file from the contents of a blob in Java