I have a rest api which returns a file.csv
and then I check that the response is 200, and datas are also in responsed.body.
But the brower didn't download the csv file.
Here is the API :
ResponseEntity<Resource> exportCsv() throws IOException {
/*
*
*/
InputStreamResource resource = new InputStreamResource(new FileInputStream(file));
HttpHeaders headers = new HttpHeaders();
headers.add("Content-disposition", "attachment; filename=sample.csv");
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(file.length())
.contentType(MediaType.parseMediaType("text/csv"))
.body(resource);
}
Here is the Angular
this.stickService.exportCsv( this.stickSearch ).subscribe(
response => this.downloadFile(response),
err => console.log('Error downloading the file.' + err.message
));
downloadFile(data: Response) {
const blob = new Blob([data], { type: 'text/csv' });
const url = window.URL.createObjectURL(blob);
window.open(url);
}
exportCsv( stickSearch: StickSearch ): Observable<any> {
const headers = this.oAuthService.putTokenInHeaders(this.headers);
let params = new HttpParams({encoder: new HttpURIEncoder()});
if (stickSearch.searchString() !== '') {
params = params
.append('search', stickSearch.searchString())
}
return this.httpClient.get(this.exportCsvUrl + '/exportCsv',
{
responseType: 'text',
params: params,
headers
});
}
I got correct data at response body.
But the download failed. 'Error downloading the file.Http failure during parsing for myURL '
Thanks for helping
It work now, this is consequence
Thanks a lot !
I can see that you are taking the output provided by the server and then building an URL off that CSV. That won't be necessary.
If you just want to download the CSV, then all you are missing is the following in your Java code:
headers.add("Content-disposition", "attachment; filename=sample.csv");
Once you have the header in place, you can get rid of the downloadFile method and probably change the this.httpClient.get request into a window.open.
See if this solves your problem and provide feedback in either case.
By default HttpClient expects that data from server will come in json format. Angular tries to parse text body as json. And that leads to exсeption. In the stickService when you do request the data, you have to specify type or result as text:
constructor (private httpClient: HttpClient){
}
public exportCsv(stickSearch: any) {
return httpClient.get('http://someurl', {responseType: 'text'});
}
Another one point that you use window.open(url). Most browsers block popup windows by default. So maybe it would be better to use an anchor element.
downloadFile(data: Response) {
const blob = new Blob([data], { type: 'text/csv' });
const url = window.URL.createObjectURL(blob);
const anchor = document.createElement('a');
anchor.download = 'myfile.txt'; // here you can specify file name
anchor.href = url;
document.body.appendChild(anchor);
anchor.click();
document.body.removeChild(anchor);
}
Here is the I'm using for this feature.
First, use 'Accept' headers.
Second, set responseType to 'text', the default is 'json'.
Third, the download code.
getCsv(): Observable<any> {
let headers = new HttpHeaders();
headers = headers.set('Accept', 'application/csv');
return this.http.get('SOME__URL', {
headers: headers,
responseType: 'text'
});
}
exportReportCSV() {
getCsv().subscribe( response => {
const link = document.createElement('a');
link.href = window.URL.createObjectURL(new Blob([response], {type: 'text/csv'}));
link.download = this.report.name + '.csv';
link.click();
});
Related
I can't download file from my friend service java code from angular that i implement and i don't know how to implement download file from angular please help me
My Code Angular
private _download$ = new Subject<void>();
**this._download$.pipe(switchMap(()=>this._downloadFile())).subscribe(resultBlob =>{
console.log("Success to Next ")
this.loadFile(resultBlob)
},(exception:any)=>{
console.log("exception: ",exception)
},()=>{
console.log("Complete: ")
})
}**
**private _downloadFile(){
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/octet-stream',
responseType : 'blob',
Accept : 'application/octet-stream',
observe : 'response'
})
};**
return this.http.get<Blob>('/col-corecollection-service/exclusion/download-file/'+this.exclusionCode,httpOptions)
}
private download(){
this._download$.next();
}
loadFile(data: any) {
var blob = new Blob([data], {type: 'application/octet-stream'});
var downloadURL = window.URL.createObjectURL(data);
window.open(downloadURL);
}**
And I have code service that my friend implement like this
#GetMapping("/exclusion/download-file/{exclCode}")
#ResponseStatus(HttpStatus.OK)
public ResponseEntity<Resource> exclusionDownloadFile(
#Size(min = 1, max = 50)
#PathVariable String exclCode
) {
if (!this.exclusionService.existsExclusionById(exclCode)) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Exclusion code " + exclCode + " not found.", null);
}
SearchExclFilenameExclBlobByIdResponse downloadFile = this.exclusionService.searchExclFilenameExclBlob(exclCode);
byte[] fileData = downloadFile.getExclBlob();
Resource resource = new InputStreamResource(new ByteArrayInputStream(fileData));
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + downloadFile.getExclFilename() + "\"")
.body(resource);
}
when i click my download button and then it can't downloadFile to me please help
Try passing responseType outside headers
Remove responseType from header:
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/octet-stream',
Accept : 'application/octet-stream',
observe : 'response'
})
};
Here is updated code for passing responseType outside headers:
return this.http.get<Blob>('/col-corecollection-service/exclusion/download-file/'+this.exclusionCode,{headers: httpOptions.headers, responseType: 'blob'})
Then if you want download use fileSaver:
var file = new Blob([data], { type: "application/octet-stream" });
this.fileSaverService.save(file);
I use ngx-filesaver which provides the fileSaver directive.
<button class="mdi mdi-download btn btn-primary"
fileSaver
[fileName]="'yourfile.xxx'"
[url]="'/col-corecollection-service/exclusion/download-file/' + this.exclusionCode"
(change)="upload($event)">
Download
</button>
I have spent the last several hours going through different Stack Overflow threads which discuss how to download excel files and passing ByteArrayOutputStreams to the front-end. I have a Spring Boot back-end which creates a custom Excel workbook and the appropriate sheets. However, it appears the binary which is returned from the back-end to the angular 6 front-end maybe incorrectly formatted. I have included the different services and controllers as well as the possibly malformed data.
Response body (Truncated value):
"PK]�uN_rels/.rels���j�0��}
�{㴃1F�^Ơ�2��l%1I,c�[��3�l
l�����H��4�R�l��·����q}*�2������;�*��
t"�^�l;1W)�N�iD)ejuD�cKz[:}g����#:�
�3����4�7N�s_ni�G�M*7�����2R�+� �2�/�����b��mC�Pp�ֱ$POyQ�抒�DsZ��IС�'un���~�PK����OPK]�uN[Content_Types].xml�SMO1��+6��m��1���G%��β...."
ExcelWriterService.java
private XSSFWorkbook workbook = new XSSFWorkbook();
public byte[] excelExporter(QueryResultsDataset<DataExportTable> data) {
List<DataExportTable> tableList = data.getTables();
List<Map<String, String>> dataRows = tableList.get(0).getRows();
OutputStream outputStream = new ByteArrayOutputStream();
... Create Excel workbook
workbook.write(outputStream);
outputStream.close();
workbook.close();
} catch (IOException ex) {
// Doing nothing
}
return ((ByteArrayOutputStream) outputStream).toByteArray();
Spring Controller
#RequestMapping(value = "<path>", method = RequestMethod.POST)
public #ResponseBody ResponseEntity<byte[]> export(#RequestBody SavedQuery request, Principal principal) {
//Run the job
QueryResultsDataset dataset = dataExportJob.getQueryResultsDataset();
ExcelWriterService ews = new ExcelWriterService();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
StringBuilder filename = new StringBuilder("hello").append(".xlsx");
headers.add("content-disposition", "inline;filename=" + filename.toString());
headers.setCacheControl("must-revalidate, post-check=0, pre-check=0");
QueryResultsDataset<DataExportTable> fixResults = (QueryResultsDataset<DataExportTable>) fixMultiplier(dataset, request);
byte [] bytes = ews.excelExporter(fixResults);
ResponseEntity<byte[]> response = new ResponseEntity<byte[]>(bytes, headers, HttpStatus.OK);
return response;
}
}
Downloader.service.ts (Angular 6 Service)
public exportExcel(search: any) {
const opts = [];
const tables = [];
this.api.download(environment._downloadDataEndPoint, search, {})
.subscribe(response => {
console.log(response);
var contentType = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
let blob = new Blob([response], {type: contentType});
let link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = "dataweb.xlsx";
link.click();
}
...
}
Api Service (Angular 6 Service)
download(url: string, body: any, header: any) {
header = JSON.parse(this._auth.getToken());
const headers = new Headers(header);
headers.append('responseType', ResponseContentType.Blob.toString());
const options = new RequestOptions({headers: headers});
const response = this.http.post(url, body, options)
.pipe(catchError((error: any) => observableThrowError(error)));
return response;
}
Any help would be great! I'm out of ideas at the moment. Thanks!
You are setting the response type in the wrong place.
Instead of
const headers = new Headers(header);
headers.append('responseType', ResponseContentType.Blob.toString());
const options = new RequestOptions({headers: headers});
Try
const headers = new Headers(header);
const options = new RequestOptions({
headers: headers,
responseType: ResponseContentType.Blob
});
See the documentation for the RequestOptions type.
I have a Spring based endpoint:
#PostMapping("/new")
public ResponseEntity house(#RequestBody Map<String ,Object> data) {
data.values().forEach(System.out::println);
return ResponseHub.defaultFound("");
}
I simply want to send a file along with some data. I'm using Ionic with Angular, so in angular I have:
let map = new TSMap();
map.set('field1', 'wan');
map.set('field2', 'two');
this.files = event.target.files;
map.set('files', this.files);
let data = map.toJSON();
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
};
let url = "http://localhost:8080/api/data";
this.http.post<HttpResponse>(url, data, httpOptions).pipe().toPromise().then(response=>{
console.log(response);
});
Sadly the output I get on my backend is just
wan
two
{0={}}
I know it's because I have Content-Type set to application/json in the http request I have on Angular. I've looked here and there, I've found a solution but allows uploading a single file and I won't be able to add any other data along with it. What would be the best solution?
Edit:
The html that calls the function that contains the Angular code:
<ion-item>
<ion-label id="profile_image" stacked>Select File</ion-label>
<ion-input type="file" accept=".png, .jpg" multiple (change)="selectFile($event)">
Select Image
</ion-input>
</ion-item>
This is where it gets the files from.
Suggested Solution:
I tried using FormData
selectFile(event) {
let formData = new FormData();
let map = new TSMap();
map.set('field1', 'wan');
map.set('field2', 'two');
let data = map.toJSON();
formData.append('data', JSON.stringify(data));
this.files = event.target.files;
formData.append('files', this.files);
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'multipart/form-data'
})
};
let url = "http://localhost:8080/api/data";
this.http.post<HttpResponse>(url, formData, httpOptions).pipe().toPromise().then(response=>{
console.log(response);
});
}
Backend:
#PostMapping("/data")
public ResponseEntity house(#RequestPart(name = "data") Map<String, Object> data,
#RequestPart(name = "files") MultipartFile[] files) throws IOException {
data.values().forEach(System.out::println);
for (MultipartFile file: files)
System.out.println(file.getInputStream().available());
return ResponseHub.defaultFound("");
}
Now it just gives me this error:
org.apache.tomcat.util.http.fileupload.FileUploadException: the
request was rejected because no multipart boundary was found
I have a problem downloading a file from a rest api using angular 6
Back-end method
#RequestMapping(value = "/print/{id}")
public ResponseEntity<byte[]> generateReport(#PathVariable("id") long id_project){
Map<String, Object> mapper = new HashMap<String, Object>();
byte[] content = exportService.export(mapper, ReportUtils.testReport, ReportUtils.FileFormat.PDF.toString());
return new ResponseEntity<>(content, Utils.getPDFHeaders("Situation_test.pdf"), HttpStatus.OK);
}
Mathod getHeader
public static HttpHeaders getPDFHeaders(String fileName) {
HttpHeaders head = new HttpHeaders();
head.setContentType(MediaType.parseMediaType("application/pdf"));
head.add("content-disposition", "attachment; filename=" + fileName);
head.setContentDispositionFormData(fileName, fileName);
head.setCacheControl("must-revalidate, post-check=0, pre-check=0");
return head;
}
My Angular Service
download(url: string): any {
let headers = new HttpHeaders();
headers = headers.append('Authorization', 'Bearer ' + this.getToken());
this.http.get(this.API_URL + url, {headers: headers}).subscribe((res) => {
const file = new Blob([res], {
type: 'application/pdf',
});
const a = document.createElement('a');
a.href = this.API_URL + (<any>res)._body;
a.target = '_blank';
document.body.appendChild(a);
a.click();
return res;
}, error => {
let alert: any = {
title: 'Notify Title',
body: 'Notify Body',
};
alert.body = error.error.message || JSON.stringify(error.error);
alert.title = error.error.error;
alert = this.alertService.handleError(error);
alert.position = 'rightTop';
console.log(error);
this.alertService.notifyError(alert);
return error;
});
}
I have already tried my API using PostMan and it word perfectly but in Angular it give me this error
HttpErrorResponse {headers: HttpHeaders, status: 200, statusText: "OK", url: "http://localhost:8080/api/projects/print/1", ok: false, …}
error: {error: SyntaxError: Unexpected token % in JSON at position 0 at JSON.parse (<anonymous>) at XMLHttp…, text: "%PDF-1.4↵%����↵3 0 obj↵<</Filter/FlateDecode/Lengt…25f1>]/Root 8 0 R/Size 10>>↵startxref↵1049↵%%EOF↵"}
headers: HttpHeaders {normalizedNames: Map(0), lazyUpdate: null, lazyInit: ƒ}
message: "Http failure during parsing for http://localhost:8080/api/projects/print/1"
name: "HttpErrorResponse"
ok: false
status: 200
statusText: "OK"
url: "http://localhost:8080/api/projects/print/1"
Try adding content-type to your request headers.
You can try this as an exemple:
let headers = new Headers({'Content-Type': 'application/pdf', 'Accept': 'application/pdf'});
My problem is that I am getting the wrong sized file on the client side. Here is my #Controller ...
#RequestMapping(value = "/download/{id}", method = RequestMethod.GET)
public ResponseEntity<?> download(final HttpServletRequest request,
final HttpServletResponse response,
#PathVariable("id") final int id) throws IOException {
try {
// Pseudo-code for retrieving file from ID.
Path zippath = getZipFile(id);
if (!Files.exists(zippath)) {
throw new IOException("File not found.");
}
ResponseEntity<InputStreamResource> result;
return ResponseEntity.ok()
.contentLength(Files.size(zippath))
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(new InputStreamResource(new FileInputStream(zippath.toFile())));
} catch (Exception ex) {
// ErrorInfo is another class, unimportant
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new ErrorInfo(ex));
}
}
... and here is my client-side code using angular-file-saver ...
$http({url: "export/download/" + exportitem.exportId, withCredentials: true})
.then(function(response) {
function str2bytes(str) {
var bytes = new Uint8Array(str.length);
for (var i=0; i<str.length; i++) {
bytes[i] = str.charCodeAt(i);
}
return bytes;
}
var blob = new Blob([str2bytes(response.data)], {type: 'application/octet-stream'});
FileSaver.saveAs(blob, "download.zip");
}, $exceptionHandler);
The original file is 935673 bytes but response.data is 900728 and passing it through the transformation to Uint8Array results in a Blob that is 900728 in size as well. Either way, the resulting saved file is 900728 bytes (34945 bytes shy). Also it is not quite the same in what gets written. It seems to slightly get bloated but then the last part just seems to be truncated. Any ideas what I might be doing wrong?
UPDATE
I just updated my controller method to be the following and got the exact same result. Grrr.
#RequestMapping(value = "/download/{id}", method = RequestMethod.GET)
public void download(final HttpServletRequest request,
final HttpServletResponse response,
#PathVariable("id") final int id) throws IOException {
// Pseudo-code for retrieving file from ID.
Path zippath = getZipFile(id);
if (!Files.exists(zippath)) {
throw new IOException("File not found.");
}
response.setContentType("application/zip");
response.setHeader("Content-Disposition",
"attachment; filename=download.zip");
InputStream inputStream = new FileInputStream(zippath.toFile());
org.apache.commons.io.IOUtils.copy(inputStream, response.getOutputStream());
response.flushBuffer();
inputStream.close();
}
So the problem turned out to be angular's $http service. I also tried jQuery's ajax method. Both gave the same result. If I instead use the native XMLHttpRequest it works correctly. So the Java code was sound. I first verified this by exposing the file directly to the internet and then both using curl and directly accessing in the browser I managed to download the file of the correct size. Then I found this solution so that I could also download the file via javascript.
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = "blob";
xhr.withCredentials = true;
xhr.onreadystatechange = function (){
if (xhr.readyState === 4) {
var blob = xhr.response;
FileSaver.saveAs(blob, filename);
}
};
xhr.send();
Why does angular or jQuery give the wrong result? I still don't know but if anyone wishes to give an answer that uses those it would be appreciated.
responseType: blob
did the trick for a zip file
Angular 2 +
this.http.get('http://localhost:8080/export', { responseType: ResponseContentType.Blob })
.subscribe((res: any) => {
const blob = new Blob([res._body], { type: 'application/zip' });
saveAs(blob, "fileName.zip");
i just stumbled over the 'responseType' in $http requests, you are probably looking for 'blob': https://docs.angularjs.org/api/ng/service/$http#usage