I have an excel file in a directory on my nodejs server - Path to the file is - ./api/uploads/appsecuritydesign/output/appsecdesign.xlsx
On click of a button in my Angular 5 component I am just trying to download the file using FileSaver.
Below is my Angular component.
Here the template code for the button in Angular that will call the saveFile() function once clicked.
<a class="btn btn-primary" (click) = "saveFile()">Download</a>
Here is the saveFile() function.
saveFile(){
console.log("In ToolInput Component: ", this.token); //Okta token
console.log("In ToolInput Component: ", this.whatamidoing); //this variable has the server FilePath
this.fileService.getappsecdesignfile(this.token, this.whatamidoing).subscribe(res => {
let blobtool5 = new Blob([res], { type: 'application/vnd.ms-excel;charset=utf-8' });
FileSaver.saveAs(blobtool5, 'Application_Security_Design.xlsx');
},
(err: HttpErrorResponse) => {
if (err.error instanceof Error) {
console.log('An error occurred:', err.error.message);
console.log('Status', err.status);
} else {
console.log(`Backend returned code ${err.status}, body was: ${err.error}`);
console.log('Status', err.status);
}
});
}
At this point I checked the console.log in the browser. They are exactly what they are supposed to be. So I am passing the correct filepath and token to getappsecdesignfile method in my fileService.
Now Lets take a look at the getappsecdesignfile method in my fileService.
getappsecdesignfile ( token, tool5filepath ) : Observable<any>{
console.log("In Service tool5filepath: ", tool5filepath);
console.log("In Service token", token);
console.log("In Service GET url: ", this.getappsecdesignfileurl);
//Since the tool5filepath has / (slashes etc) I am encoding it below.
let encodedtool5filepath = encodeURIComponent(tool5filepath);
console.log('Encoded File Path: ', encodedtool5filepath);
let req = new HttpRequest('GET', this.getappsecdesignfileurl,{params: new HttpParams().set('path', encodedtool5filepath)},{headers: new HttpHeaders().set('Accept', 'application/vnd.ms-excel').set('Authorization', token)});
console.log(req);
return this.http.request(req);
}
That's all there is to the fileService method. Lets look at the console.logs from this method from the browser to ensure all the correct values are being set.
Now Lets take a look at the request itself before we go to the server part.
As far as I am concerned the headers are set correctly, params are set correctly. Two issues I see is that Angular's interceptors probably sets the responseType: json and adds a param op:s to my request.
Node/Express Server code.
app.get('/getappsecdesignfile', function(req, res){
console.log("In get method app security design");
accessTokenString = req.headers.authorization;
console.log("Okta Auth Token:", accessTokenString);
console.log("Tool5 File Path from received from Angular: ", req.query.path); //this is where the server console logs shows Tool5 File Path after decoding: ./undefined
oktaJwtVerifier.verifyAccessToken(accessTokenString)
.then(jwt => {
// the token is valid
console.log(jwt.claims);
res.setHeader('Content-Disposition', 'attachment; filename= + Application_Security_Design.xlsx');
res.setHeader('Content-Type', 'application/vnd.ms-excel');
let tool5filepath = './' + decodeURIComponent(req.query.path);
console.log("Tool5 File Path after decoding: ", tool5filepath);
res.download(tool5filepath);
}).catch(err => {
// a validation failed, inspect the error
res.json({success : false, message : 'Authorization error.'});
});
});
If I use Postman the api works fine. However somewhere between Angular to Node communication something happens that I don't understand.
Below is what the server logs. (Big question how does this become undefined)?
Tool5 File Path from received from Angular: undefined
Tool5 File Path after decoding: ./undefined
Error: ENOENT: no such file or directory, stat '<dirpath>/undefined'
Here is what I see in the browser log:
zone.js:2933 GET http://localhost:3000/getappsecdesignfile 404 (Not Found)
toolinput.component.ts:137 Backend returned code 404, body was: <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
</head>
<body>
<pre>Error: ENOENT: no such file or directory, stat '<dirpath>/undefined'</pre>
</body>
</html>
Then the browser downloads a xlsx file that is corrupt and cannot be opened.
I have checked the file resides in the directory and is ready to be downloaded.
Thanks for any tips that can help me resolve this issue.
Finally figured it out.
2 specific changes made this work.
Change # 1 - Setting responseType : 'blob' and defining the params and headers first and then using them in http.get. (http is nothing but an object of type HttpClient from angular/common/http that has been injected into the service class.
getappsecdesignfile ( token, tool5filepath ) : Observable<any>{
console.log("In Service tool5filepath: ", tool5filepath);
console.log("In Service token", token);
console.log("In Service GET url: ", this.getappsecdesignfileurl);
let encodedtool5filepath = encodeURIComponent(tool5filepath);
console.log('Encoded File Path: ', encodedtool5filepath);
let getfileparams = new HttpParams().set('filepath', encodedtool5filepath);
let getfileheaders = new HttpHeaders().set('Accept', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet').set('Authorization', token);
return this.http.get(this.getappsecdesignfileurl, {responseType: 'blob', params: getfileparams, headers: getfileheaders});
}
Change # 2 - Component code - FileSaver. For some reason type: 'application/vnd.ms-excel' did not work in FileSaver. Here the res is nothing but the response from the http.get call.
let blobtool5 = new Blob([res], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
FileSaver.saveAs(blobtool5, 'Application_Security_Design.xlsx');
来源:https://stackoverflow.com/questions/48553958/how-to-download-an-excel-xlsx-file-using-angular-5-httpclient-get-method-with