response.data is undefined when downloading files - java

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.

Related

SpringBoot : how to download file from a MicroService that returns a ResponseEntity<Resource>?

I'm trying to download a File (PDF or Excel) returned by a Spring Boot Micro Service.
So, I have a GWT application (let's call it "A") and a Micro Service.
The problem is : the micro service creates the required file (perfect) but my browser doesn't display the "Save as" popup. Nothing happens.
Here is what I have so far :
#GetMapping(value = "/report")
public ResponseEntity<Resource> generateFullReport(HttpServletResponse response, HttpServletRequest request,
#RequestParam("name") String name, #RequestParam("output") String output) throws BirtException {
log.info("Generating full report: " + name + "; format: " + output);
EnumOutputType format = EnumOutputType.from(output);
String filePath = birtReportManagerService.generateMainReport(name, format, response, request);
File file = new File(filePath);
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename="+file.getName());
headers.add(HttpHeaders.CACHE_CONTROL, "no-cache, no-store, must-revalidate");
headers.add(HttpHeaders.PRAGMA, "no-cache");
headers.add(HttpHeaders.EXPIRES, "0");
ByteArrayResource resource = null;
try {
resource = new ByteArrayResource(Files.readAllBytes(Paths.get(filePath)));
} catch (IOException e) {
e.printStackTrace();
}
ResponseEntity responseEntity = ResponseEntity.ok()
.headers(headers)
.contentLength(file.length())
.contentType(MediaType.parseMediaType(MediaType.APPLICATION_OCTET_STREAM_VALUE))
.body(resource);
// file.delete();
return responseEntity;
}
And :
#Override
public void generateBirtReport(String reportName, String reportFormat, HashMap<String, String> parameters) {
logger.debug("Entering REST generateBirtReport {}", reportName+"."+reportFormat);
LinkedMultiValueMap<String, String> multiValueMap = new LinkedMultiValueMap<>();
for(Map.Entry<String, String> entry : parameters.entrySet()){
multiValueMap.add(entry.getKey(), entry.getValue());
}
URI uri = UriComponentsBuilder.fromUriString(URL_GLOBAL_REST + "/v1/reports/report")
.queryParam("name", reportName)
.queryParam("output", reportFormat)
.queryParams(multiValueMap)
.build()
.encode()
.toUri();
HttpHeaders httpHeaders = new HttpHeaders();
HttpEntity<String> request = new HttpEntity<>(httpHeaders);
ResponseEntity<Resource> response = restTemplate.exchange(uri, HttpMethod.GET, request, Resource.class);
}
I would like to see that dialog box :
Version : SpringBoot : 2.1.5 / Java : 8

StreamingResponseBody returning empty file

I'm trying to create a rest service to download files from a repository, using Springboot.
I'm trying to return a ResponseEntity with StreamingResponseBody, to return the file that i get from the repository, as an InputStream.
This is the current code i have:
#GetMapping(path = "/downloadFile")
public ResponseEntity<StreamingResponseBody> downloadFile(#RequestParam(value = "documentId") String documentId,
HttpServletRequest request, HttpServletResponse response) throws InterruptedException, IOException {
InputStream is = downloadService.getDocument(documentId);
StreamingResponseBody out = outputStream -> {
outputStream.write(IOUtils.toByteArray(is));
};
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", "text/csv");
headers.add("Content-Disposition", "attachment; filename=" + documentId);
headers.add("Pragma", "no-cache");
headers.add("Cache-Control", "no-cache");
return (new ResponseEntity<>(out, headers, HttpStatus.OK));
}
When I consume this endpoint, using directly the browser, or postman, the file that is downloaded comes empty.
I understand that the OutputStream is written to asynchronously (Async is enabled in the config class).
How can I consume this service and get the file completely written, the way it comes from the repository I'm using ? ( if possible using Postman, just for testing purposes)
Am i building the service correctly?
I have modified the code bit little, in my documentId is the name of the file to be downloaded. I have tested, it is working fine. Check below the code.
#GetMapping(path = "/downloadFile")
public ResponseEntity<StreamingResponseBody> downloadFile(
#RequestParam(value = "documentId") String documentId,
HttpServletRequest request,
HttpServletResponse response)
throws InterruptedException, IOException {
String dirPath = "E:/sure-delete/"; //Directory having the files
InputStream inputStream = new FileInputStream(new File(dirPath + documentId));
final StreamingResponseBody out =
outputStream -> {
int nRead;
byte[] data = new byte[1024];
while ((nRead = inputStream.read(data, 0, data.length)) != -1) {
System.out.println("Writing some bytes of file...");
outputStream.write(data, 0, nRead);
}
};
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", "text/csv");
headers.add("Content-Disposition", "attachment; filename=" + documentId);
headers.add("Pragma", "no-cache");
headers.add("Cache-Control", "no-cache");
return ResponseEntity.ok().headers(headers).body(out);
}

How to force the response of RestController to csv file

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.

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