如何使用 Angular 5 HttpClient get 方法和 Node/Express 后端下载 excel (xlsx) 文件?
How to download an excel (xlsx) file using Angular 5 HttpClient get method with Node/Express backend?
我的 nodejs 服务器上的目录中有一个 excel 文件 - 文件路径是 - ./api/uploads/appsecuritydesign/output/appsecdesign.xlsx
单击我的 Angular 5 组件中的按钮后,我只是尝试使用 FileSaver 下载文件。
下面是我的Angular组件。
此处是 Angular 中按钮的模板代码,单击后将调用 saveFile() 函数。
<a class="btn btn-primary" (click) = "saveFile()">Download</a>
这是 saveFile() 函数。
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);
}
});
}
此时我在浏览器中查看了console.log。他们正是他们应该成为的样子。因此,我将正确的文件路径和令牌传递给 fileService 中的 getappsecdesignfile 方法。
现在让我们看一下我的文件服务中的 getappsecdesignfile 方法。
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);
}
这就是 fileService 方法的全部内容。让我们从浏览器中查看此方法的 console.logs,以确保设置了所有正确的值。
现在让我们先看看请求本身,然后再转到服务器部分。
就我而言,headers 设置正确,参数设置正确。我看到的两个问题是 Angular 的拦截器可能设置了 responseType: json 并向我的请求添加了一个参数 op:s。
Node/Express 服务器代码。
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.'});
});
});
如果我使用 Postman,api 工作正常。然而,在 Angular 与节点通信之间的某个地方发生了一些我不明白的事情。
以下是服务器记录的内容。 (大问题这是怎么变成未定义的)?
Tool5 File Path from received from Angular: undefined
Tool5 File Path after decoding: ./undefined
Error: ENOENT: no such file or directory, stat '<dirpath>/undefined'
这是我在浏览器日志中看到的内容:
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>
然后浏览器下载了一个损坏的无法打开的xlsx文件。
我已检查文件位于目录中,可以下载了。
感谢任何可以帮助我解决此问题的提示。
终于想通了。
2 项具体更改使这项工作成功。
更改 # 1 - 设置 responseType : 'blob' 并首先定义参数和 headers,然后在 http.get 中使用它们。 (http 只不过是来自 angular/common/http 的 HttpClient 类型的 object,已注入服务 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});
}
更改 # 2 - 组件代码 - FileSaver。由于某种原因,类型:'application/vnd.ms-excel' 在 FileSaver 中不起作用。这里的 res 只是 http.get 调用的响应。
let blobtool5 = new Blob([res], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
FileSaver.saveAs(blobtool5, 'Application_Security_Design.xlsx');
我的 nodejs 服务器上的目录中有一个 excel 文件 - 文件路径是 - ./api/uploads/appsecuritydesign/output/appsecdesign.xlsx
单击我的 Angular 5 组件中的按钮后,我只是尝试使用 FileSaver 下载文件。
下面是我的Angular组件。
此处是 Angular 中按钮的模板代码,单击后将调用 saveFile() 函数。
<a class="btn btn-primary" (click) = "saveFile()">Download</a>
这是 saveFile() 函数。
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);
}
});
}
此时我在浏览器中查看了console.log。他们正是他们应该成为的样子。因此,我将正确的文件路径和令牌传递给 fileService 中的 getappsecdesignfile 方法。
现在让我们看一下我的文件服务中的 getappsecdesignfile 方法。
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);
}
这就是 fileService 方法的全部内容。让我们从浏览器中查看此方法的 console.logs,以确保设置了所有正确的值。
现在让我们先看看请求本身,然后再转到服务器部分。
就我而言,headers 设置正确,参数设置正确。我看到的两个问题是 Angular 的拦截器可能设置了 responseType: json 并向我的请求添加了一个参数 op:s。
Node/Express 服务器代码。
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.'});
});
});
如果我使用 Postman,api 工作正常。然而,在 Angular 与节点通信之间的某个地方发生了一些我不明白的事情。
以下是服务器记录的内容。 (大问题这是怎么变成未定义的)?
Tool5 File Path from received from Angular: undefined
Tool5 File Path after decoding: ./undefined
Error: ENOENT: no such file or directory, stat '<dirpath>/undefined'
这是我在浏览器日志中看到的内容:
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>
然后浏览器下载了一个损坏的无法打开的xlsx文件。
我已检查文件位于目录中,可以下载了。
感谢任何可以帮助我解决此问题的提示。
终于想通了。
2 项具体更改使这项工作成功。
更改 # 1 - 设置 responseType : 'blob' 并首先定义参数和 headers,然后在 http.get 中使用它们。 (http 只不过是来自 angular/common/http 的 HttpClient 类型的 object,已注入服务 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});
}
更改 # 2 - 组件代码 - FileSaver。由于某种原因,类型:'application/vnd.ms-excel' 在 FileSaver 中不起作用。这里的 res 只是 http.get 调用的响应。
let blobtool5 = new Blob([res], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
FileSaver.saveAs(blobtool5, 'Application_Security_Design.xlsx');