【发布时间】:2019-02-12 19:40:07
【问题描述】:
我目前有一个 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 驱动的单页应用程序。我已经搜索了有关如何处理来自 API 端点的文件的各种方法,并且似乎很多都围绕着获取 Blob 和创建一个内联 URL,然后通过 JavaScript 中的window.open() 导航到该 URL。或者在内存中创建a 标签,然后调用click()。
我的问题:最新的 Angular 真的没有办法开箱即用地处理这个问题吗?我不是 Angular 专家,但我认为their site 上会有一个示例或一些用于将下载文件提供给浏览器的内置机制。然而,似乎只是涉及很多黑客行为。
我当前的 WIP 解决方案确实将文件返回到浏览器以供下载,但无论 API 中指定的文件名如何,它都会作为临时文件名(一些类似于 GUID 的东西)下载:
以下是我的组件和服务类中的内容。我更新了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 解决方案的混搭)。有没有更好的办法? “最佳实践”之类的方式?
【问题讨论】:
-
我不认为有任何开箱即用的解决方案。看看这是否有助于使用
createObjectURL:stackoverflow.com/a/51635727/1160794 -
@David 谢谢!我现在正在使用它来生成名称。仍然会留下问题,看看是否有一些晦涩难懂的 Angular-y 方法来做到这一点。
标签: javascript c# angular typescript asp.net-core