You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在使用ajax="false"的<p:commandButton>下载文件时启用<p:blockUI>?

解决文件下载时添加BlockUI加载提示的方案

针对JSF2.0 + PrimeFaces3.3 + Liferay6.1.1的环境,由于Ajax请求无法直接处理文件下载响应,我们可以通过两种方式实现下载时的BlockUI加载提示:

方法一:直接通过非Ajax按钮结合JS显示BlockUI

这种方式适合文件生成速度较快的场景,点击按钮时先显示加载提示,再触发同步下载请求:

  1. 保留BlockUI定义:
<p:blockUI block=":form" widgetVar="blockWidget">
    <h:graphicImage value="/css/images/ajax-loader.gif" />
</p:blockUI>
  1. 修改下载按钮,通过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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 00:22:18