В настоящее время у меня есть конечная точка API ASP.NET Core 2.1, которую пользователи могут использовать для загрузки файла 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, использует одностраничное приложение с угловым управлением. Я искал различные способы обработки файлов, поступающих с конечных точек API, и многие, похоже, вращаются вокруг получения BLOB-объектов и создания встроенного URL-адреса, который затем переходит на JavaScript с помощью window.open()
. Или создание в памяти тегов a
для вызова click()
on.
Мой вопрос: неужели у последнего Angular действительно нет способа справиться с этим из коробки? Я не специалист по Angular, но я бы подумал, что на их сайте будет пример или какой-нибудь встроенный механизм для подачи загруженного файла в браузер. Тем не менее, похоже, что здесь много хакеров.
Мое текущее решение WIP возвращает файл в браузер для загрузки, но независимо от имени файла, указанного в API, оно загружается как временное имя файла (что-то похожее на GUID):
Ниже приведено то, что я имею как в своих классах компонентов, так и в классах обслуживания. Я обновил метод downloadFile()
, чтобы использовать ответ, предоставленный в этом SO-ответе , чтобы заставить работать дружественные имена файлов, но я все равно предпочел бы найти менее хакерское решение.
// 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
, чтобы заставить это работать (смесь Found On Internet решений). Есть ли способ лучше? Способ "Best Practices"?