Экспорт файла с произвольным именем - PullRequest
2 голосов
/ 10 марта 2020

Я хочу реализовать этот пример кода, который используется для экспорта данных из Spring BE.

@GetMapping("export/{ids}")
    public void export(HttpServletResponse response, @PathVariable List<Integer> ids) {

        List<Transactions> transactions = (List<Transactions>) transactionService.findAll(ids);
        List<TransactionExcelEntry> entries = transactions.stream().map(payment_transaction_mapper::toExcel).collect(Collectors.toList());

        List<String> headers = Arrays.asList("Id", "Name", "Type", "Created at");
        try {
            response.addHeader("Content-disposition", "attachment; filename=Transactions.xlsx");
            response.setContentType("application/vnd.ms-excel");
            new SimpleExporter().gridExport(headers, entries, "id, name", response.getOutputStream());
            response.flushBuffer();

        } catch (IOException ex) {
            LOG.debug("parsing of transactions failed");
        }
    }

Кнопка экспорта:

<button class="dropdown-item" (click)="export()">XLS</button>

Функциональность экспорта:

export() {
    var newPagination = new Pagination();
    newPagination.size = this.pagination.size * this.pagination.total
    this.transactionService.search(newPagination, this.formGroup.value)
        .subscribe(result => {
            this.formGroup.enable();
            const query = result.content.map(t => t.id).join(',');
            this.transactionService.exportRows(query).subscribe(data => {
                const a = document.createElement('a');

                a.href = window.URL.createObjectURL(data);
                a.download = 'export.xls';
                a.click();
            });
        }, (error) => {
            this.formGroup.enable();
        });
  }

exportRows(query) {
    return this.http.get(`/api/transactions/export`, { responseType: 'blob' });
}

Я хочу сгенерировать имя файла в Java BE и загрузить его из Angular FE. Как этот функционал может быть реализован?

Ответы [ 2 ]

2 голосов
/ 13 марта 2020

Вы можете получить имя файла своего большого двоичного объекта, открыв заголовок ответа и получив Content-Disposition.

. Для этого измените немного свой вызов HttpClient.get, предоставив дополнительную опцию observe: 'response'.

Чтобы прояснить ситуацию, мы создаем выделенный ExportResponse для предоставления только необходимых данных нашему компоненту / или другому методу обслуживания:

export type ExportResponse = {
  blob: Blob,
  fileName: string
}

exportRows возвращает Response Headers:

exportRows(query): ExportResponse {
  return this.http.get(`/api/transactions/export`, { 
    observe: 'response',
    responseType: 'blob' 
  }.pipe(
    map(response => {
      const contentDisposition = response.headers.get('content-disposition');
      return {
        blob: response.body,
        fileName: getContentDispositionFileName(contentDisposition)
      }
    })
  );
}

Метод getContentDispositionFileName отвечает за извлечение имени файла из полученного заголовка:

getContentDispositionFileName(contentDisposition: string) {
  let filename = 'default-file-name';

  var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
  var matches = filenameRegex.exec(contentDisposition);
  if (matches != null && matches[1]) {
    filename = matches[1].replace(/['"]/g, '');
  }

  return filename;
}

Этот шаблон RegExp взят из Ответ Зимнего солдата , и извлекает часть имени файла.

Затем вы можете использовать ExportResponse в своем первоначальном методе:

this.transactionService.exportRows(query).subscribe(response => {
  const a = document.createElement('a');
  a.href = window.URL.createObjectURL(response.blob);
  a.download = response.fileName;
  a.click();
});

Важное примечание

Если у вас включен CORS, Осторожно, чтобы ваш бэкэнд мог отправлять и авторизовать ваш интерфейс для доступа к заголовку Content-Disposition.

Для этого добавьте Access-Control-Expose-Headers: Content-Disposition в свой ответ.

response.addHeader(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, HttpHeaders.CONTENT_DISPOSITION);
0 голосов
/ 13 марта 2020

Если вы хотите, чтобы имя файла генерировалось автоматически, вы можете попробовать следующий подход

export(filename) {
  this.transactionService.search(newPagination, this.formGroup.value)
        .subscribe(result => {
            this.formGroup.enable();
            const timeStamp: number = new Date().getTime();
            const query = result.content.map(t => t.id).join(',');
            this.transactionService.exportRows(query).subscribe(data => {
                const a = document.createElement('a');

                a.href = window.URL.createObjectURL(data);
                a.download = `${fileName}-${timeStamp}.xls`;
                a.click();
            });
        }, (error) => {
            this.formGroup.enable();
        });
}

При нажатии кнопки вы можете отправить имя файла или из сервисного звонка вы можете получить имя файла с сервера

<button class="dropdown-item" (click)="export('Transaction')">XLS</button>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...