How to force the response of RestController to csv file - java

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.

Related

response.data is undefined when downloading files

I tried to use spring boot to pass files to vue. The files can be opened, but there's noting in it.
I checked the res.data, and it turned out that it is undefined.
Here's the spring boot's code:
The contorller:
#GetMapping("/download/{path}")
public ResponseEntity<InputStreamResource> downloadFiles(#PathVariable String path, HttpServletResponse response) throws IOException {
//fileUtils.getFile(path,response);
return fileUtils.downloadFile(path);
}
The FileUtils:
public ResponseEntity<InputStreamResource> downloadFile(String path)
throws IOException {
String filePath = System.getProperty("user.dir") + "\\files\\" + path;
FileSystemResource file = new FileSystemResource(filePath);
HttpHeaders headers = new HttpHeaders();
headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
headers.add("Content-Disposition", String.format("attachment; filename=\"%s\"", file.getFilename()));
headers.add("Pragma", "no-cache");
headers.add("Expires", "0");
return ResponseEntity
.ok()
.headers(headers)
.contentLength(file.contentLength())
.contentType(MediaType.parseMediaType("application/octet-stream"))
.body(new InputStreamResource(file.getInputStream()));
}
The Vue's code:
handleDownload(path,fileName){
request.get('/files/download/'+path+fileName, {responseType: 'blob'}).then(res => {
console.log(res.data)
fileDownload(res.data, fileName);
}).catch((res)=>{
console.log('download error');
}
)
}
I guess the reason there is only'undefined' in the file is that res.data is'undefined'. But I don't understand why res.data is undefined.
fileDownload(res, fileName); works. But I don't know why.

Download pdf from restful web services

I am writing a rest service which will download the pdf when user access this service on users machine. below is the code of my controller class.
When I tried to access below url in browser, it is downloading .JSON file and not PDF file.
URL:- http://localhost:8080/test-service/downloadPDF?userId=abcd&requestingLocation=6&orderId=06006030
I have tried to use same code which was mentioned in below sites. It seems the straight forward code but not sure why it is not working.
http://www.java2novice.com/restful-web-services/jax-rs-download-file/
https://www.javatpoint.com/jax-rs-file-download-example
Below is method in controller.
#GET
#RequestMapping(value = "/downloadPDF")
#Produces("application/pdf")
public #ResponseBody Response downloadDocument(#RequestParam("userId") String userId,#RequestParam("requestingLocation") String requestinglocation,#RequestParam("orderId") String orderId) {
String path = "C:\\opt\\autobol\\logs\\autobol\\test2.pdf";
File file = new File(path);
ResponseBuilder response = Response.ok((Object) file);
response.header("Content-Disposition","attachment; filename=\"javatpoint_pdf.pdf\"");
return response.build();
}
It should download pdf file
below is the content of downloaded file (file name downloadPDF.json):
{
"entity":"C:\\opt\\autobol\\logs\\autobol\\test2.pdf",
"status":200,
"metadata":{
"Content-Disposition":[
"attachment; filename=\"javatpoint_pdf.pdf\""
]
},
"annotations":null,
"entityClass":"java.io.File",
"genericType":null,
"length":-1,
"language":null,
"location":null,
"lastModified":null,
"date":null,
"closed":false,
"cookies":{
},
"statusInfo":"OK",
"stringHeaders":{
"Content-Disposition":[
"attachment; filename=\"javatpoint_pdf.pdf\""
]
},
"links":[
],
"entityTag":null,
"allowedMethods":[
],
"mediaType":null,
"headers":{
"Content-Disposition":[
"attachment; filename=\"javatpoint_pdf.pdf\""
]
}
}
in downloadPDF.json file
Try this
HttpResponse response = HttpContext.Current.Response;
response.ClearContent();
response.Clear();
Response.ContentType = "application/pdf";
Response.AppendHeader("Content-Disposition", "attachment; filename=" + Path.GetFileName(strPath));
response.TransmitFile(strPath); //File
response.Flush();
response.End();
Response.ContentType = ContentType;
Response.AppendHeader("Content-Disposition", "attachment; filename=" + Path.GetFileName(strPath));
Response.WriteFile(strPath);
Response.End();
Here is a spring boot example.
you can refer this example https://docs.spring.io/spring-boot/docs/current/reference/html/getting-started-first-application.html and attach the below class in the project
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.*;
import org.springframework.http.*;
import java.io.File;
import java.nio.file.Files;
#Controller
public class DemoController {
#RequestMapping(value="/getpdf", method=RequestMethod.GET)
public ResponseEntity<byte[]> getPDF() {
File file = new File("sample.pdf"); // change to relative path
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_PDF);
String filename = "output.pdf";
headers.setContentDispositionFormData(filename, filename);
headers.setCacheControl("must-revalidate, post-check=0, pre-check=0");
ResponseEntity<byte[]> response = null;
try {
response = new org.springframework.http.ResponseEntity<>(Files.readAllBytes(file.toPath()), headers, org.springframework.http.HttpStatus.OK);
} catch (java.io.IOException e) {
e.printStackTrace();
}
return response;
}
}
Try this using Spring RestController
#PostMapping(value = "/slip", headers = "Accept=application/json",
produces = "application/pdf")
public ResponseEntity<Resource> generateSalarySlip(HttpServletRequest request,
#RequestBody Locale locale) {
...
...
File file = new File(fileName);
HttpHeaders header = new HttpHeaders();
header.setContentType(MediaType.parseMediaType("application/pdf"));
header.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=salaryslip.pdf");
header.add("Cache-Control", "no-cache, no-store, must-revalidate");
header.add("Pragma", "no-cache");
header.add("Expires", "0");
InputStream targetStream = new FileInputStream(file);
Resource resource = new InputStreamResource(targetStream)
return ResponseEntity.ok()
.headers(header)
.contentLength(file.length())
.contentType(MediaType.parseMediaType("application/pdf"))
.body(resource);
}

How to download response as a JSON file created from Java Object

I am writing a code in Spring Boot where i want to download response as a .json file(Json file) which should not be created in any of my project directory but it should be created on the fly from java Object
#RequestMapping(value = "/", method = RequestMethod.GET,produces = "application/json")
public ResponseEntity<InputStreamResource> downloadPDFFile()
throws IOException {
User user = new User();
user.setName("Nilendu");
user.setDesignation("Software Engineer");
createJsonFile(user);
ClassPathResource jsonFile = new ClassPathResource("a.json");
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()
.contentLength(jsonFile.contentLength())
.contentType(
MediaType.parseMediaType("application/octet-stream"))
.body(new InputStreamResource(jsonFile.getInputStream()));
}
void createJsonFile(User user) {
ObjectMapper mapper = new ObjectMapper();
try {
// Convert object to JSON string and save into a file directly
File file = new File("src/main/resources/a.json");
System.out.println(file.exists()+" ++++");
mapper.writeValue(file, user);
System.out.println("File Created");
} catch (JsonGenerationException e) {
e.printStackTrace();
} catch (JsonMappingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
I am able to do this with above code but every time i make request it creates a new file a.json in src/main/resource directory Which i don't want . i don't want to create this file in any directoy but still i should be able to download the file
Then don't write it to a file!
byte[] buf = mapper.writeValueAsBytes(user);
return ResponseEntity
.ok()
.contentLength(buf.length)
.contentType(
MediaType.parseMediaType("application/octet-stream"))
.body(new InputStreamResource(new ByteArrayInputStream(buf)));
EDIT
To prompt browser for .json file type add a header
.header("Content-Disposition", "attachment; filename=\"any_name.json\"")
you dont need to create a file, just convert the object to json using Gson,
Gson gson = new Gson ();
String jsonString = gson.toJson (user);

HTTP 406 downloading a file with rest call

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")

"Content-Disposition" and file name in headers with AngularJS

I use angularjs 1.3.14
I have a java REST services produce a xml file with this header: "Content-Disposition", "attachment; filename=yourFileName";
I need take the file with the name of my file on AngularJS.
I have this code:
$http.get('/someUrl').success(function(data, status, headers){
var myHeaders = headers();
...
});
but in myHeaders have only {content-type="application/xml"}. I need find "Content-Disposition", "attachment; filename=yourFileName"
Java Services:
#GET
#Path(EXPORT_URL)
#Produces(MediaType.APPLICATION_XML)
public Response export(#Context HttpServletRequest request) {
String userName = request.getRemoteUser();
if (userName != null) {
...
ResponseBuilder response = Response.ok(myObject);
response.header("Content-Disposition", "attachment; filename=myFile.xml");
return response.build();
} else {
return Response.status(Status.FORBIDDEN).build();
}
}
2 years after, I find a solution:
#RequestMapping(value = "/export", method = RequestMethod.GET, produces = MediaType.APPLICATION_XML)
public ResponseEntity<String> export(...
HttpHeaders headers = new HttpHeaders();
headers.setAccessControlExposeHeaders(Collections.singletonList("Content-Disposition"));
headers.set("Content-Disposition", "attachment; filename=" + filename);
return new ResponseEntity<>(exportedContent, headers, HttpStatus.OK);
This is a server side CORS issue. You need to enable this:
"Access-Control-Expose-Headers", "Content-Disposition"

Categories