- PVSM.RU - https://www.pvsm.ru -
Приветствую, дорогой читатель! В данной статье я хотел изложить одну проблему, с которой я столкнулся при разработке, а также способ ее решения. Решение конечно не самое безупречное, но имеет место быть. Если вам что-то не понравиться, или вы знаете решение лучше, прошу большими огурцами меня не бить, так как я еще мал и зелен. Бейте маленькими с комментариями и поучениями.
Задача в следующем: у нас есть система, в которой есть страница на которой отображена некоторая отчетность. Там необходимо реализовать формирование Excel файла и выгрузку его для пользователя.
Страница имеет следующую приблизительную структуру, которая отображена на верхнем рисунке.
Причем особенностью является то, что таблица очень большая, и выгружать в Excel нужно много записей.
А при нажатии на строку выводиться панелька для редактирования записи.
Код формирующий файл:
public void generatePatientsExcel() {
try{
log.info("Generating patients excel");
FacesContext facesContext = FacesContext.getCurrentInstance();
ExternalContext externalContext = facesContext.getExternalContext();
HttpServletResponse response = (HttpServletResponse) externalContext.getResponse();
//Create excel file
Workbook wb = new HSSFWorkbook();
//
//
//
//
//
// Header
response.setHeader("Content-disposition", "attachment; filename=1.xls");
response.setHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0");
response.setCharacterEncoding("UTF-8");
response.setContentType("application/vnd.ms-excel");
//Send Response
ServletOutputStream responseOutputStream = response.getOutputStream();
wb.write(responseOutputStream);
responseOutputStream.flush();
responseOutputStream.close();
facesContext.responseComplete();
log.info("Generating patients excel comlplete");
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
Проблема состоит в том, что при выгрузке файла формирование его происходит некоторое время, и если в этот момент нажать на строку то мы получим:
org.jboss.seam.ConcurrentRequestTimeoutException: Concurrent call to conversation
Чтобы этого не случилось необходимо навесить на onclick перед формированием файла JavaScript функцию, которая отображает Loader. И после action скрыть его тоже JavaScript функцией навешеной на oncomplete. Вроде все просто, не правда ли?
Так как мы формируем файл динамически, мы не сохраняем его на диск, что могло б облегчить задачу, путем возвращения не HttpServletResponse, в котором файл, а переходом по ссылке, где сохранен файл на диске. Но это в свою очередь будет есть дисковое пространство сервера, поэтому динамическое формирование файла это то, что нам нужно.
Загвоздка в том, что кнопка h:commandButton не имеет oncomplete. И вообще все елементы «h:» не имеют этого event. Подумав немного: «Вообще зачем нам этот h:commandButton? Ведь у нас есть волшебный a4j:commandButton».
Реализуем выгрузку файла через a4j:commandButton, это выглядит так:
<a4j:commandButton onclick="showLoader();" image="exportExcel.png"
action="#{importToExcelBean.getExcelFile()}" oncomplete="hideLoader();"/>
И все вроде бы прекрасно, но это только на первый взгляд. Так как a4j:commandButton выгружает HttpServletResponse в текущую страницу.
Пользователь выгружает файл в Excel формате, а ему открывается страница с корявками. «Непорядок»- сказал пользователь, ударив кулаком в клавиатуру, матерясь. И разочаровался в нашей системе на всю жизнь.
Можно попытаться на h:commandButton навесить a4j:support, который на onsubmit будет отображать лоадер, при action формировать файл, а на oncomplete скрывать лоадер. Но проблема в том что a4j:support не может вернуть HttpServletResponse с сервера. Он это делает тоже в страницу. Так как и все элементы a4j возвращают HttpServletResponse в страницу.
Незадача получается. Что делать, никто не знает, интернет молчит, пользователи плачут, и сам сидишь как укакался. А ведь такой простой event, но его так не хватает.
Решение пришло в голову коллеге. Все довольно просто.
<a4j:commandButton onclick="showLoader();" image="exportExcel.png"
action="#{importToExcelBean.generateExcel()}" oncomplete="hideLoader()"/>
<h:commandButton id="exelFileButton" style=" visibility: hidden;"
action="#{importToExcelBean.returnResponse()}" />
<script type="text/javascript" >
function clickButtonGetExelFile(){jQuery("#exelFileButton").click();}
</script>
<a4j:commandButton onclick="showLoader();" image="exportExcel.png"
action="#{importToExcelBean.generatePatientsExcel()}" oncomplete="hideLoader();clickButtonGetExelFile();"/>
Готово, пользователь получает файл в том виде, в котором должен получать. Критическая ситуация разрешена.
Автор: jorikburlakov
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/java/10176
Нажмите здесь для печати.