如何在Angular 8中将Excel文件作为API响应下载

20
我有一个API,它返回一个Excel文档作为响应。请求将是一个简单的JSON。
我在Google上搜索并找到了一些代码库来下载文件,并在我的应用程序中使用了它,但是我遇到了一些错误,无法找到解决办法。以下是我的代码基础:

component.ts

import rxjs/Rx;

details = {id: 75,name: "some name"}

this.nameService.getData(this.details).subscribe(response => {
this.downloadFile(response);
})

downloadFile(data: Response) {
  const blob = new Blob([data], { type: '.xlsx' });
  const url= window.URL.createObjectURL(blob);
  window.open(url);
}

nameService.ts

getData(postData) {
  return this.httpService.post('https://localhost:8080/getFile/', postData);
}

httpService.ts

constructor(private http: HttpClient)
post(apiEndpoint: string, request: any) :Observable<any> {
 return this.http.post<any>(apiEndpoint,request);
}

使用上述代码库时,我遇到了以下两个错误,并且文件无法下载。

  1. 错误信息如下:

在创建 Blob(const blob = new Blob([data], { type: '.xlsx' });)时出现“类型 Response 无法分配给类型 Blobpart”的错误。

  1. 如果我将数据更改为任意类型 (downloadFile(data: any)),上述错误(1)将消失,但我会得到一个 httperror 响应,内容为“语法错误:在 json 的位置 0 处出现意外的标记 P,位于 json.parse”。

如果有人找到了解决上述问题的方法,请帮忙提供帮助。

7个回答

15
  1. 在请求中设置响应头{ responseType: 'blob'}

  2. 在创建Blob文件时,需要传递正确的MIME类型(例如:application/octet-streamapplication/vnd.openxmlformats-officedocument.spreadsheetml.sheet)。

component.ts

downloadFile(data: Response) {
  const blob = new Blob([data], { type: 'application/octet-stream' });
  const url= window.URL.createObjectURL(blob);
  window.open(url);
}

13
如何获取特定文件名的文件?比如说“NameList.xlsx”? - knbibin
当您使用此功能时,网页浏览器会下载一种文件。尽管我从API发送的文件(是正确的)在其中找不到。 - Yannick Mussche
当您使用此功能时,网页浏览器会下载一种文件。尽管我从API发送的文件是正确的,但在其中找不到它。 - undefined

13

添加头信息{ responseType: 'blob'}

就像这样:

this.http.post<any>(apiEndpoint,request,{ responseType: 'blob'} )

我已将HttpHeader设置为blob,但问题仍然存在。 - knbibin

11

虽然这里的大多数答案都建议使用 { responseType: 'blob' },但我建议使用 responseType: 'arraybuffer' 代替 blob 并与 observe: 'response' 结合使用。你的代码应该如下所示:

this.httpClient.get(resource, {
  headers: this.httpHeaders, // Any custom client side headers like Authorization
  observe: 'response',
  responseType: 'arraybuffer'
});

这样做的好处有两个:

  1. ArrayBuffer 是一个通用的字节流,可以使用 Content-Type 创建 blobarraybuffer 的 Blob 对象。
  2. observe: 'response' 还会包括在解析的响应中从服务器设置的响应头。

我使用这种方法将文件名和 arraybuffer 的 MIME 类型放置在 Content-DispositionContent-Type 响应头中,然后在客户端使用通用下载服务。

此外,建议使用 File 对象代替仅使用 Blob,这样可以为其提供文件名,例如:

public downloadFile(response: any, fileName?: string) {
    const blob = new Blob([response.body], { type: response.headers.get('content-type') });
    fileName = fileName || response.headers.get('content-disposition').split(';')[0];
    const file = new File([blob], fileName, { type: response.headers.get('content-type') });
    saveAs(file);
}

这也将解决 @knbibin 提出的使用自定义文件名的问题。raised


这个工作正常,但是文件名不起作用。 - Pradeep
@suhas 你能告诉我 saveAs 是什么吗? - tech_geek
@tech_geek 这是一个来自于Node软件包"file-saver"的方法。你可以轻松地这样导入:import { saveAs } from 'file-saver'; - Suhas Bharadwaj
@SuhasBharadwaj 我认为它没有包含在我的Angular中,所以它没有被导入。 - tech_geek
@tech_geek 你可以使用npm install file-saver。 - Suhas Bharadwaj

6
在请求中设置响应头{ responseType: 'blob'}
使用以下函数来下载Excel文件。
response - API响应, fileName - Excel文件名。
downloadExcel(response, fileName) {
    // Doing it this way allows you to name the file
    var link = document.createElement('a');
    link.href = window.URL.createObjectURL(response);
    link.download = fileName;
    link.click();
  }

当您使用此功能时,Web浏览器会下载一种文件(一种包含一些XML数据的文件)。尽管我从API发送的文件(是正确的)在其中找不到。 - Yannick Mussche
当您使用此功能时,网页浏览器会下载一种文件(其中包含一些XML数据)。尽管我从API发送的文件是正确的,但在其中却找不到。 - undefined

2

使用以下代码下载文件

downloadFile(data: Response) {
  const blob = new Blob([data], { type: 'application/octet-stream' });
  fs.saveAs(blob, fileName + '.xlsx');
}

10
根据免费词典,"fs"有195个定义。在此处查看: https://acronyms.thefreedictionary.com/FS因此,为了清晰起见,请稍微做出一个虽小但重要的努力,澄清 fs.saveAs(...) 的含义。只是粘贴代码是不够好的。这实际上是一个 npm 包 (https://www.npmjs.com/package/file-saver),它允许你从浏览器将流保存到文件系统,这回答了上面发布的问题。 - Yazan Khalaileh
它是{import * as fs from 'file-saver';},但我不认为这个库还存在。 - Yannick Mussche
它是{import * as fs from 'file-saver';},但我不认为这个库还存在。 - undefined
你仍然可以通过以下方式安装它:npm i file-saver,然后它会完美地运行。 - Yannick Mussche

1

您需要在您的get请求中添加{ responseType : 'application/octet-stream'},无需调用post方法。

{ responseType : 'application/octet-stream'}

在 .ts 文件中

DownLoadExcel(){
    this.MprService.downLoadRFQInfoExcel(this.revisionId).subscribe(data =>{this.downloadFile(data)});
}
    
downloadFile(data: Blob) { 
    const contentType = 'application/vnd.openxmlformats-ficedocument.spreadsheetml.sheet'; 
    const blob = new Blob([data], { type: contentType });
    const url = window.URL.createObjectURL(blob);
    window.open(url);
}

0
如果有人只想从资产中下载文件,他们可以使用这段代码:
<a href="./assets/files/file.xlsx" target="_blank"></a>

但是你需要在 angular.json 中提及位置。

"architect": {
   "build": {
      "options": {
    "assets": [
                  "src/assets/files"
              ]
      }
   }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接