Angular 5/6:从ASP.NET Core API HTTP处理文件下载(带友好文件名)

问题描述 投票:1回答:1

我目前有一个ASP.NET Core 2.1 API端点,用户可以点击以下载CSV文件:

[HttpPost("Gimme")]
public IActionResult Gimme([FromBody] MyOptions options)
{
    MemoryStream reportStream = _reportGenerator.GenerateReportStream(options.StartDate, options.EndDate);

    return new FileStreamResult(reportStream, "text/csv") { FileDownloadName = "report.csv" };
}

我可以通过POSTMan测试它,它返回一个包含正确数据的CSV,没问题。

输入Angular。我们与API交互的网站使用Angular-Driven单页应用程序。我已经搜索了有关如何处理来自API端点的文件的各种方法,并且很多似乎都围绕着获取Blob并创建内联URL,然后通过JavaScript中的window.open()导航到该内联URL。或者创建内存中的a标签然后调用click()

我的问题:最新的Angular真的无法处理开箱即用的问题吗?我不是Angular专家,但我认为在their site上有一个例子,或者有一些内置机制可以将下载的文件提供给浏览器。然而,似乎只涉及很多hackery。

我目前的WIP解决方案确实将文件返回到浏览器进行下载,但无论API中指定的文件名如何,都将其作为临时文件名(一些看起来像GUID的东西)下载:

CSV download with random file name

以下是我在Component和Service类中的内容。我更新了downloadFile()方法,以使用this SO answer中提供的答案来获得友好的文件名,但我仍然宁愿找到一个不那么hacky的解决方案。

// Component

DownloadReport(options: ReportOptions) {
    this._service.getCSV(options).subscribe(data => this.downloadFile(data));
}

downloadFile(blob: Blob) {
    const fileName = 'report.csv';
    if (navigator.msSaveBlob) {
        // IE 10+
        navigator.msSaveBlob(blob, fileName);
    } else {
        const link = document.createElement('a');
        const url = URL.createObjectURL(blob);
        link.setAttribute('href', url);
        link.setAttribute('download', fileName);
        link.style.visibility = 'hidden';
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        URL.revokeObjectURL(url);
    }
}

// Service

getCSV(reportOptions: ReportOptions): Observable<any> {
    let headers = new HttpHeaders();
    headers = headers.set('Accept', 'text/csv');
    return this._httpClient
        .post(`${this.apiRoot}/Gimme`, reportOptions, { headers: headers, responseType: 'blob' })
        .catch(this.handleError);
}

正如你所看到的,我目前正在实施createObjectURL hack来实现这一功能(一种Found On Internet解决方案)。有没有更好的办法?一种“最佳实践”的方式?

javascript c# angular typescript asp.net-core
1个回答
1
投票

这就是我使用的。这与你使用的基本相同,但是使用Anchor打字看起来有点干净。我研究了正确的方法来做这个很长一段时间,并找不到一个非hacky方式来做到这一点。

  download(): void {
    this.service.getFile({ id: id, name: name })
      .subscribe(data => {
        if (window.navigator && window.navigator.msSaveOrOpenBlob) {
          //save file for IE
          window.navigator.msSaveOrOpenBlob(data, name);
        } else {
          const objectUrl: string = URL.createObjectURL(data);
          const a: HTMLAnchorElement = document.createElement('a') as HTMLAnchorElement;

          a.href = objectUrl;
          a.download = name;
          document.body.appendChild(a);
          a.click();

          document.body.removeChild(a);
          URL.revokeObjectURL(objectUrl);
        }
      });
  }
© www.soinside.com 2019 - 2024. All rights reserved.