Angular 5/6: обработка загрузки файла (с дружественным именем файла) из ASP.NET Core API HTTP - PullRequest
0 голосов
/ 07 сентября 2018

В настоящее время у меня есть конечная точка 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):

CSV download with random file name

Ниже приведено то, что я имею как в своих классах компонентов, так и в классах обслуживания. Я обновил метод 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"?

1 Ответ

0 голосов
/ 07 сентября 2018

Это то, что я использую. Это в основном то же самое, что вы используете, но выглядит немного чище для меня, набирая Anchor. Я долго искал правильный способ сделать это и не мог найти нехакерский способ сделать это.

  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);
        }
      });
  }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...