如何在使用ajax="false"的<p:commandButton>下载文件时启用<p:blockUI>?
解决文件下载时添加BlockUI加载提示的方案
针对JSF2.0 + PrimeFaces3.3 + Liferay6.1.1的环境,由于Ajax请求无法直接处理文件下载响应,我们可以通过两种方式实现下载时的BlockUI加载提示:
方法一:直接通过非Ajax按钮结合JS显示BlockUI
这种方式适合文件生成速度较快的场景,点击按钮时先显示加载提示,再触发同步下载请求:
- 保留BlockUI定义:
<p:blockUI block=":form" widgetVar="blockWidget"> <h:graphicImage value="/css/images/ajax-loader.gif" /> </p:blockUI>
- 修改下载按钮,通过
onclick手动触发BlockUI显示:
<p:commandButton value="#{lbl['lbl.btn.download']}" style="float:right;" ajax="false" onclick="blockWidget.show();" action="#{backingBean.downloadZipFile}" />
注意:由于同步请求完成后页面不会执行JS回调,BlockUI可能会在下载对话框弹出后一直显示,直到用户完成下载操作或刷新页面。如果文件生成耗时较长,建议用方法二。
方法二:分两步处理(Ajax准备文件 + 非Ajax触发下载)
这种方式更可靠,先通过Ajax在后台生成文件并保存到临时目录,完成后再触发同步下载,同时控制BlockUI的显示/隐藏:
1. 后台ManagedBean修改
添加文件准备方法和下载方法,将生成的文件临时存储在Liferay的临时目录:
private String tempZipPath; // 存储临时文件路径 // 准备下载文件:生成zip并保存到临时目录 public String prepareDownload() { try { String sourceFile = "C:/Users/dev/Documents/test.txt"; String zipFileName = "TestZip_" + System.currentTimeMillis() + ".zip"; // 获取Liferay临时目录 String tempDir = PortalUtil.getPortalTempDir(); tempZipPath = tempDir + File.separator + zipFileName; // 生成zip文件到临时目录 ZipOutputStream zipOut = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(tempZipPath))); File fileToZip = new File(sourceFile); FileInputStream fis = new FileInputStream(fileToZip); ZipEntry zipEntry = new ZipEntry(fileToZip.getName()); zipOut.putNextEntry(zipEntry); byte[] bytes = new byte[1024]; int length; while((length = fis.read(bytes)) >= 0) { zipOut.write(bytes, 0, length); } zipOut.closeEntry(); zipOut.close(); fis.close(); } catch (Exception e) { e.printStackTrace(); } return "success"; } // 下载临时文件并清理 public void downloadPreparedZip() throws IOException { PortletResponse portalresponse = (PortletResponse) FacesContext.getCurrentInstance().getExternalContext().getResponse(); HttpServletResponse response = PortalUtil.getHttpServletResponse(portalresponse); response.setContentType("application/zip"); response.setHeader("Content-Disposition", "attachment; filename=TestZip.zip"); File tempFile = new File(tempZipPath); FileInputStream fis = new FileInputStream(tempFile); OutputStream out = response.getOutputStream(); byte[] bytes = new byte[1024]; int length; while((length = fis.read(bytes)) >= 0) { out.write(bytes, 0, length); } out.flush(); fis.close(); response.flushBuffer(); FacesContext.getCurrentInstance().responseComplete(); // 下载完成后删除临时文件 tempFile.delete(); }
2. 页面代码调整
添加一个隐藏的非Ajax按钮用于触发下载,点击主按钮时先执行Ajax准备文件,完成后触发隐藏按钮并关闭BlockUI:
<p:blockUI block=":form" widgetVar="blockWidget"> <h:graphicImage value="/css/images/ajax-loader.gif" /> </p:blockUI> <!-- 隐藏的非Ajax下载按钮 --> <p:commandButton id="hiddenDownloadBtn" ajax="false" action="#{backingBean.downloadPreparedZip}" style="display:none;" /> <!-- 主操作按钮 --> <p:commandButton value="#{lbl['lbl.btn.download']}" style="float:right;" ajax="true" onstart="blockWidget.show();" oncomplete="blockWidget.hide(); $('#form\\:hiddenDownloadBtn').click();" action="#{backingBean.prepareDownload}" />
这种方式可以确保BlockUI在文件生成期间显示,生成完成后自动隐藏并触发下载,体验更流畅。
内容的提问来源于stack exchange,提问作者Asyraf Dayan
相关产品推荐
相关产品推荐

