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

HCL Portal 9.5中PDF下载按钮触发Servlet重复调用问题问询

在HCL Portal 9.5中PDF Servlet被重复调用的问题排查与解决

问题描述

我在HCL Portal 9.5上部署了一款包含PDF Servlet的JSF Portlet,该Servlet用于在新标签页打开PDF文件。目前遇到的问题是:点击新标签页中的下载按钮时,该Servlet会被再次调用。请问这是什么原因?能否避免该现象?

Servlet类代码

public class PdfDownloadServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    // 存储SNMP错误
    private static HashMap<SnmpNapakeEnum, Date> snmpMap = new HashMap<SnmpNapakeEnum, Date>();

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        processRequest(req, resp);
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        processRequest(req, resp);
    }

    protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String param1 = ... // 读取URL参数

        try {
            writeOutputStream(response); // 注:此处方法调用缺少param1参数,与方法定义不匹配,请检查
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
            OutputStream os = null;
            try {
                os = response.getOutputStream();
                os.write(e.getMessage().getBytes("UTF-8"));
            } finally {
                if (ObjectUtils.isNotEmpty(os)) {
                    os.close();
                    os = null;
                }
            }
        }
    }

    private void writeOutputStream(HttpServletResponse response, String param1) throws IOException, ParseException {
        OutputStream os = null;
        try {
            byte[] data = ... // 调用EJB方法获取PDF字节数组

            response.setContentType("application/pdf");
            response.setContentLength(data.length);

            response.setHeader("Content-Disposition", "inline;filename=Sample.pdf");

            os = response.getOutputStream();
            os.write(data);
        } finally {
            if (ObjectUtils.isNotEmpty(os)) {
                os.close();
                os = null;
            }
        }
    }
}

web.xml配置

<servlet>
    <description>Pdf download</description>
    <display-name>PdfDownloadServlet</display-name>
    <servlet-name>PdfDownloadServlet</servlet-name>
    <servlet-class>si.nijz.rizddz.portlet.skupno.servlets.ObvestiloOStDelavcaServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>PdfDownloadServlet</servlet-name>
    <url-pattern>/PdfDownloadServlet</url-pattern>
</servlet-mapping>

补充实现代码

后台Bean动作监听器

public void onBtnGeneratePdf() {
    String date1AsStr = "2020-02-13";
    
    PrimeFaces.current().executeScript("generatePdf('" + date1AsStr + "');");
}

JavaScript触发函数

generatePdf : function(date1AsStr) {
    try {
            var path = $('[id$=hidContextPath]').val() + '/PdfDownloadServlet?date1AsStr=' + date1AsStr;
            window.open(path);
    } catch (e) {
        ...
    }
    return true;
}

JSF按钮代码

<p:commandButton id="btnGenerate" icon="ui-icon-document" actionListener="#{myBean.onBtnGeneratePdf}" value="Generate" />

原因分析

  1. 浏览器PDF查看器默认行为:当通过Content-Disposition: inline在新标签页打开PDF时,浏览器内置PDF查看器的下载按钮本质是重新向服务器发起资源请求,因此会再次调用Servlet。
  2. 缺少缓存控制配置:当前Servlet未设置任何缓存响应头,浏览器无法复用已下载的PDF内容,每次点击下载都会重新请求服务器。

解决方案

方案一:添加缓存响应头(推荐,保留原打开方式)

在writeOutputStream方法中添加缓存控制头,让浏览器缓存PDF内容,点击下载时直接读取本地缓存,不再触发Servlet调用:

private void writeOutputStream(HttpServletResponse response, String param1) throws IOException, ParseException {
    OutputStream os = null;
    try {
        byte[] data = ... // 调用EJB方法获取PDF字节数组

        response.setContentType("application/pdf");
        response.setContentLength(data.length);
        // 设置缓存有效期为1小时,可根据需求调整
        response.setHeader("Cache-Control", "public, max-age=3600");
        response.setHeader("Expires", new Date(System.currentTimeMillis() + 3600 * 1000).toString());
        response.setHeader("Last-Modified", new Date().toString());

        response.setHeader("Content-Disposition", "inline;filename=Sample.pdf");

        os = response.getOutputStream();
        os.write(data);
    } finally {
        if (ObjectUtils.isNotEmpty(os)) {
            os.close();
            os = null;
        }
    }
}

注意:若PDF内容随请求参数变化,需确保缓存能区分不同参数的PDF,或调整缓存有效期避免用户获取旧内容。

方案二:直接触发下载(改变打开方式)

将Content-Disposition改为attachment,点击按钮后直接下载PDF,无需在新标签页打开,自然不会触发后续请求:

response.setHeader("Content-Disposition", "attachment;filename=Sample.pdf");

方案三:预生成静态PDF资源

生成PDF后保存到服务器静态资源目录或缓存中,新标签页打开静态文件URL,点击下载时请求静态资源,不会触发Servlet。需额外处理:

  • 生成唯一文件名区分不同参数的PDF
  • 定时清理过期临时文件

内容的提问来源于stack exchange,提问作者peterremec

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 02:37:02