I want to implement this code example which is used to export data from 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");
}
}
Export button:
<button class="dropdown-item" (click)="export()">XLS</button>
Export functionality:
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' });
}
I want to generate the name of the file into the Java BE and download it from the Angular FE. How this functionality can be implemented?
You can retrieve the file name of your blob by accessing the response headers and retrieving Content-Disposition
.
To do that, change a little your HttpClient.get
call, by providing additional option observe: 'response'
.
To make it clear, we create a dedicated ExportResponse
to expose only needed data to our component/or other service method :
export type ExportResponse = {
blob: Blob,
fileName: string
}
The exportRows
retrieves 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)
}
})
);
}
The getContentDispositionFileName
method is in charge to extract the filename from the header received:
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;
}
This RegExp pattern comes from Winter Soldier's answer, and extracts the filename's part.
Then you can use ExportResponse
in your initial method:
this.transactionService.exportRows(query).subscribe(response => {
const a = document.createElement('a');
a.href = window.URL.createObjectURL(response.blob);
a.download = response.fileName;
a.click();
});
If you have CORS
enabled, be careful to allow your backend to send and authorize your frond-end to access to Content-Disposition
header.
To do that, add Access-Control-Expose-Headers: Content-Disposition
in your response.
response.addHeader(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, HttpHeaders.CONTENT_DISPOSITION);