download a file from rest api is giving me some garbage value - java

I am trying to download a file from rest API, I am writing code in Java and react. but when i call that rest api it is not downloading that file instead gives me some garbage
#POST
#Path("/{loginId}")
#Produces(MULTIPART_FORM_DATA)
#Consumes(APPLICATION_JSON)
public Response downloadExportedFile(#PathParam("loginId") String loginId, ExportFileDTO fileDetails) {
File exportFolder = new File("C://directory");
File[] listOfFiles = exportFolder.listFiles();
for (File listOfFile : listOfFiles) {
if (listOfFile.getName().equals(fileDetails.getFileName())) {
InputStream is = new FileInputStream(listOfFile.getAbsolutePath());
byte[] buffer = IOUtils.toByteArray(is);
return Response.ok(listOfFile)
.header("content-disposition", "attachment; filename=" + new File(listOfFile.getName()).getName())
.type(MediaType.APPLICATION_OCTET_STREAM_TYPE).build();
}
}
It should download the file instead it is giving me output as
PK!b�h^�[Content_Types].xml �(����N�0E�H�C�-Jܲ#5��Q>�ēƪc[�ii����B�j7���{2��h�nm���ƻR����U^7/���%��rZY�#1__�f��q��R4D�AJ�h>����V�ƹ�Z�9����NV�8ʩ����ji){^��-I�"{�v^�P!XS)bR�r��K�s(�3�`c�0��������7M4�����ZƐk+�|\|z�(���P��6h_-[�#�!���Pk���2n�}�?�L��� ��%���d����dN"m,�ǞDO97�~��ɸ8�O�c|n���E������B��!$}�����;{���[����2���PK!�U0#�L_rels/.rels �(���MO�0��H�����ݐBKwAH�!T~�I����$ݿ'T�G�~����<���!��4��;#�w����qu*&r�Fq���v�����GJy(v��*����K��#F��D��.W ��=��Z�MY�b���BS�����7��ϛז��
?�9L�ҙ�sbgٮ|�l!��USh9i�b�r:"y_dl��D���|-N��R"4�2�G�%��Z�4�˝y�7 ë��ɂ�����PK!

You have to change the associated mimetype by changing the the parameter of the #Produces annotation which basically describes what type of data you transmit in your response.
It should become:
#Produces("application/vnd.ms-excel")

According to this other stackoverflow question you should change the #Produces annotation to #Produces(MediaType.APPLICATION_OCTET_STREAM).
According to this second stackoverflow question you are asking an impossible question.
Out of curiosity I reproduced your problem here : see the full gist
If you change #POST to #GET it starts working
If you keep #POST, it has to be posted from a real form and can't post application/json
Finally, posting application/json means React is doing a programmatic XmlHTTPRequest. The above gist shall convince you there is no user prompt in that case
When you say it 'is giving me output', you're not telling where and how the post was requested . You will have to adapt that part.

actually It is APPLICATION_OCTET_STREAM response for a file. we have to handle download functionality at client side AS per Nate's answer here, the response of Ajax request is not recognized by a browser as a file. It will behave in the same way for all Ajax responses. You need to trigger the download popup manually.
downloadFile(fileDetails) {
let username = getUserName();
return fetch(`/files/${username}`, {
method: 'POST',
body: JSON.stringify(fileDetails)
}).then(response => {
return response.blob();
}).then(response => {
let blob = new Blob([response], {type: 'application/octet-stream'});
let fileUrl = window.URL.createObjectURL(blob);
Files.triggerDownload(fileUrl, fileDetails.fileName);
}).catch((error) => {
//myerror
});
}
static triggerDownload(url, fileName) {
let a = document.createElement('a');
a.setAttribute('href', url);
a.setAttribute('download', fileName);
a.click();
}
This will download the file at client machine

Related

Save a PDF generated by SpringBoot in Angular

I am trying to save a PDF using Angular and Spring Boot.
When I make an API call, my Java code is fetching the data from the database and transforming it to a byte-stream. This stream is sent as response.
if(format.equals(Constant.PDF_FORMAT)) {
ByteArrayInputStream stream = reportPDF.generateReportDocument(dtos);
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Disposition", "inline; filename=report.pdf");
return ResponseEntity.ok()
.headers(headers)
.contentType(MediaType.APPLICATION_PDF)
.body(new InputStreamResource(stream));
}
I have to use this response and save the data into a PDF.
Component.ts
public getReports(type?: string): void {
this.params['expected-format'] = type;
if (type === 'json') {
this.Service.getPilotReports(this.params).subscribe((res) => {
this.reportsData = res;
this.pilotBankSpinnerService.closeSpinner();
});
} else {
this.Service.customGetForDownload(this.params).subscribe(
(data: Blob) => {
var file = new Blob([data], { type: 'application/pdf' });
var fileURL = URL.createObjectURL(file);
window.open(fileURL);
var a = document.createElement('a');
a.href = fileURL;
a.target = '_blank';
a.download = 'reports.pdf';
document.body.appendChild(a);
a.click();
},
(error) => {
console.log('getPDF error: ', error);
}
);
}
}
Service.ts
public customGetForDownload<blob, T>(url: string, params: any): any {
const headers = new HttpHeaders({ 'Content-Type': 'application/json', responseType: 'blob' });
const httpParams = this.http.constructParams(params);
const absoluteUrl = this.getAbsoluteUrl(url);
return this.httpClient.get(absoluteUrl, {
headers: headers,
params: httpParams,
responseType: 'blob' as 'json',
observe: 'response',
});
}
Though the file is getting saved. When I try to open the file, it says "Failed to load pdf document".
Syntax Issues
First I see a syntax error:
missing argument in method-call: ByteArrayInputStream stream = reportPDF.generateReportDocument(dtos, ); (after the comma)
With this syntax error you most likely receive a compilation-error on console.
Assume this is a lapse and you can fix it to something like ByteArrayInputStream stream = reportPDF.generateReportDocument(dtos); then it should compile.
Boots without errors? Then test the endpoint!
Assume further your server application boots and runs without errors, then you could test the HTTP-endpoint with a HTTP-call.
You can test using a HTTP-client like CURL, postman or maybe even a browser.
Then you should receive a response with HTTP status code 200 and the body containing the PDF-file as binary with MIME-type application/pdf and specified header Content-Dispositon.
The browser is expected to prompt you with a download-dialogue.
Responding with a binary in Spring
Your InputStreamResource is a valid way, but you should be confident when using it.
In a Spring controller method, you can return the binary data in different types:
ResponseEntity<byte[]> as byte-array
ResponseEntity<ByteArrayOutputStream> as stream (not input-stream for reading input)
ResponseEntity<Resource> as abstract binary content, see Spring's Resource
ResponseEntity<File> as entire file
See also
Spring boot Angular2 file download not working
PDF Blob is not showing content, Angular 2
Return generated pdf using spring MVC
There are also some response-directed ways especially in Spring:
return a InputStreamResource as you did
return a StreamingResponseBody is very convenient
write to a HttpServletResponse, probably the oldest way
See: How To Download A File Directly From URL In Spring Boot
From input to output
Remember: Input is for reading (e.g. from a request), output is for writing (e.g. to a response). So you need an output type, like byte[] or ByteArrayOutputStream etc for your response-body.
When reading input into ByteArrayInputStream stream you could copy from that input to an output-stream with e.g. Apache-Commons IOUtils: IOUtils.copy(in, out);.
Or simply return the byte-array: byte[] data = stream.readAllBytes();
See: Java InputStream to Byte Array and ByteBuffer | Baeldung

Downloaded file from server is different than original one saved previously

I cannot find out why the mp3 file is different after download from my server than original one saved previously there.
This is my controller method. The content of file (byte[] content) is identical with original file on this stage - the original file is the same as file retrieved from database (checked in debugger).
#ResponseBody
#RequestMapping(method = RequestMethod.GET, value = "/{sampleId}/file")
public HttpEntity<byte[]> getFile(#PathVariable Long sampleId) {
ResourceEntity resourceEntity = testSampleRepository.getFile(sampleId);
byte[] content = resourceEntity.getContent();
String fileName = resourceEntity.getFileName();
HttpHeaders header = new HttpHeaders();
header.setContentType(new MediaType("audio", "mpeg"));
header.set(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=" + fileName.replace(" ", "_"));
header.setContentLength(content.length);
return new HttpEntity<byte[]>(content, header);
}
This is how files differ (the left is original one):
Why passing using HTTP distors my file? Should mediaTypes enforce certain encoding? (there was no difference with "audio/mpeg" mediaType and without it).
It should work, if you set the produces = "application/octet-stream" attribute (MediaType.APPLICATION_OCTET_STREAM). Otherwise, you are trapped by Spring's converter framework.
You may want to have a look here, seems your problem is very similar: Spring MVC: How to return image in #ResponseBody? .

How to download 10GB file with Angularjs, Java, Tomcat,Spring and REST?

When I download small file , everything is OK , but I need some way to download large files . If file large, Blob didn't created , haven't enough memory.
Download file without save on client , directly save to disk with many requests on the server or something like that.
My code on server is :
#RequestMapping(value = "/oneFile/{name}", method = RequestMethod.GET)
public void getOneFile( #PathVariable("name") String name, HttpServletResponse response,HttpServletRequest request) {
....
InputStream in = new FileInputStream(new File(file.getAbsolutePath()));
org.apache.commons.io.IOUtils.copy(in, response.getOutputStream());
response.flushBuffer();
On client and this is work for small size:
backupFileServer.downloadOneFileBrow(data)
.success(function(databack) {
var file = new Blob([ databack ], {
type : 'application/csv'
});
var fileURL = URL.createObjectURL(file);
var a = document.createElement('a');
a.href = fileURL;
a.target = '_blank';
a.download = data;
document.body.appendChild(a);
a.click();
})
.error(function() {
alert($scope.DOWNLOAD_ERROR);
});
I tried something like this but didn't work :
var a = document.createElement('a');
a.href = 'data:attachment/csv;charset=utf-8,' + encodeURI(databack);
a.target = '_blank';
a.download = data;
document.body.appendChild(a);
a.click();
How someone idea how to do this or some example or code ....
Thank you in advance
You need to split your file into multiple files and rebuild the file with your server.
Because it's bad practice to upload a big file without splitting it, the user can be disconnect at 99% of the upload and he need to upload the whole file again.
An example here : https://flowjs.github.io/ng-flow/
A good example here : http://ryansouthgate.com/2015/12/24/upload-amazon-s3-using-angularjs/ with Amazon S3 and their SDK.

Sending in-memory generated .docx files from server to client with Spark

I am creating a web application using the Spark Java framework. The front-end is developed using AngularJS.
I want to generate a .docx file on the server (in-memory) and send this to the client for download.
To achieve this I created an angular service with the following function being called after the user clicks on a download button:
functions.generateWord = function () {
$http.post('/api/v1/surveys/genword', data.currentSurvey).success(function (response) {
var element = angular.element('<a/>');
element.attr({
href: 'data:attachment;charset=utf-8;application/vnd.openxmlformats-officedocument.wordprocessingml.document' + response,
target: '_blank',
download: 'test.docx'
})[0].click();
});
};
On the server, this api call gets forwarded to the following method:
public Response exportToWord(Response response) {
try {
File file = new File("src/main/resources/template.docx");
FileInputStream inputStream = new FileInputStream(file);
byte byteStream[] = new byte[(int)file.length()];
inputStream.read(byteStream);
response.raw().setContentType("data:attachment;chatset=utf-8;application/vnd.openxmlformats-officedocument.wordprocessingml.document");
response.raw().setContentLength((int) file.length());
response.raw().getOutputStream().write(byteStream);
response.raw().getOutputStream().flush();
response.raw().getOutputStream().close();
return response;
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
I have tried to solve this in MANY different ways and I always end up with a corrupted 'test.docx' that looks like this:
Solved it by using blobs and specifying the response type as 'arraybuffer' in the $http.post api call. The only bad thing with this solution (as far as I know) is that it doesn't play well with IE, but that's a problem for another day.
functions.generateWord = function () {
$http.post('/api/v1/surveys/genword', data.currentSurvey, {responseType: 'arraybuffer'})
.success(function (response) {
var blob = new Blob([response], {type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'});
var url = (window.URL || window.webkitURL).createObjectURL(blob);
var element = angular.element('<a/>');
element.attr({
href: url,
target: '_blank',
download: 'survey.docx'
})[0].click();
});
};
I think what went wrong was that the byte stream got encoded as plain text when I tried to create a URL with:
href: 'data:attachment;charset=utf-8;application/vnd.openxmlformats-officedocument.wordprocessingml.document' + response
thus corrupting it.
When using blobs instead, I get a "direct" link to the generated byte stream and no encoding is done on it since the response type is set to 'arraybuffer'.
Note that this is just my own reasoning of why things went wrong with the original code. I might be terribly wrong, so feel free to correct me if that's the case.

Execute after sending http response Java

I want to know if this can be done.
I got a RESTful service sending a javax.ws.rs.core.Response response like this
File filer = new File("C:\tempfile.txt");
return Response.ok(filer, MediaType.APPLICATION_OCTET_STREAM).header("content-disposition", "attachment; filename=\"" + newName + "\"").build();
I want to delete file after http response has been sent to the client (making filer a temporary file on server).
I thank your help in advance.
You probably shouldn't be using a temporary file to begin with. If you want to send generated binary data and don't want to keep it all in memory, pass a StreamingOutput as the entity when calling ok().
make it in finally block
try {
File filer = new File("C:\tempfile.txt");
return Response.ok(filer, MediaType.APPLICATION_OCTET_STREAM).header("content-disposition", "attachment; filename=\"" + newName + "\"").build();
} finally {
// delete file
}

Categories