Hi all!
I want to solve the following problem. The java method unloads data from the database and writes the data to the writer. Since you don’t want to store this object in memory and don’t wait on the client side until the entire report is completely generated, then transfer the data loading to the stream and generate a file on the client side.
I have my java code:
public void generateIntervalReportCsv(Appendable fileOutputStream, String dateFrom, String dateTo, String searchType) throws Exception {
try (Printer csvPrinter = new PrinterCsvAdapter(new CSVPrinter(fileOutputStream, CSVFormat.EXCEL.builder().setDelimiter(";").setHeader("PERIOD", "STATUS", "RECORDS COUNT") .build()))) {
generateIntervalReport(csvPrinter, dateFrom, dateTo, searchType);
}
}
Rest-controller:
@GetMapping("/interval-report-file-csv")
public ResponseEntity<StreamingResponseBody> generateReport(@RequestParam String dateFrom,
@RequestParam String dateTo,
@RequestParam String period,
HttpServletResponse response) {
response.setHeader(
"Content-Disposition",
"attachment;filename=My-Report.csv");
StreamingResponseBody stream = out -> {
try {
processService.generateIntervalReportCsv(new OutputStreamWriter(out), dateFrom, dateTo, period);
} catch (Exception e) {
throw new RuntimeException("Error - ", e);
}
};
return ResponseEntity.ok(stream);
}
Javascript:
function downloadCsvReport() {
const minutesToAbort = 5;
const awaiting_response_time_in_ms = 1000 * 60 * minutesToAbort;
if (!is_downloadIntervalReport_alreadyPushed) {
is_downloadIntervalReport_alreadyPushed = true;
let dateFrom = document.getElementById("periodStartDate").value;
let dateTo = document.getElementById("periodEndDate").value;
let searchType = document.getElementById("period").value;
let buttonStyle = document.getElementById("csvButtonId").style;
buttonStyle.backgroundColor = "gray";
let url = "/audit-migrator/process/interval-report-file-csv" + "?dateFrom=" + dateFrom + "&dateTo=" + dateTo + "&period=" + searchType;
downloadIntervalReport_timeOfStartRequest = new Date();
fetch(url, {signal: AbortSignal.timeout(awaiting_response_time_in_ms)})
.then(async (res) => {
if (res.status >= 200 && res.status < 300) {
return res;
} else {
throw new Error(await res.text());
}
})
.then(res => {
res.blob().then(file => {
let tempUrl = URL.createObjectURL(file);
const aTag = document.createElement("a");
aTag.href = tempUrl;
aTag.download = decodeURI(res.headers.get('Content-Disposition').split('UTF-8\'\'')[1]);
document.body.appendChild(aTag);
aTag.click();
URL.revokeObjectURL(tempUrl);
aTag.remove();
});
}).catch((reason) => {
if (reason.name === "AbortError") {
console.log("Request aborted");
} else {
console.error(reason.message);
}
}).finally(() => {
is_downloadIntervalReport_alreadyPushed = false;
buttonStyle.backgroundColor = "#d17b2e";
});
}
}
I have written the following javascript function in my html page:
function downloadCsvReport() {
const minutesToAbort = 5;
const awaiting_response_time_in_ms = 1000 * 60 * minutesToAbort;
if (!is_downloadIntervalReport_alreadyPushed) {
is_downloadIntervalReport_alreadyPushed = true;
let dateFrom = document.getElementById("periodStartDate").value;
let dateTo = document.getElementById("periodEndDate").value;
let searchType = document.getElementById("period").value;
let buttonStyle = document.getElementById("csvButtonId").style;
buttonStyle.backgroundColor = "gray";
let url = "/interval-report-file-csv" + "?dateFrom=" + dateFrom + "&dateTo=" + dateTo + "&period=" + searchType;
downloadIntervalReport_timeOfStartRequest = new Date();
fetch(url, {signal: AbortSignal.timeout(awaiting_response_time_in_ms)})
.then(async (res) => {
if (res.status >= 200 && res.status < 300) {
return res;
} else {
throw new Error(await res.text());
}
})
.then(res => {
res.blob().then(file => {
let tempUrl = URL.createObjectURL(file);
const aTag = document.createElement("a");
aTag.href = tempUrl;
aTag.download = decodeURI(res.headers.get('Content-Disposition').split('UTF-8\'\'')[1]);
document.body.appendChild(aTag);
aTag.click();
URL.revokeObjectURL(tempUrl);
aTag.remove();
});
}).catch((reason) => {
if (reason.name === "AbortError") {
console.log("Request aborted");
} else {
console.error(reason.message);
}
}).finally(() => {
is_downloadIntervalReport_alreadyPushed = false;
buttonStyle.backgroundColor = "#d17b2e";
});
}
}
When you click on the report download button, an empty file with the name undefined.txt starts to load.
In html debugging, this happens on the line :
fetch(url, {signal: AbortSignal.timeout(awaiting_response_time_in_ms)})
What am I doing wrong?