You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

服务器环境下后台执行含JavaScript的JSP以自动生成PDF的可行方案咨询

服务器环境下后台执行含JavaScript的JSP以自动生成PDF的可行方案咨询

看起来你现在的核心痛点是:依赖用户登录会话的Edge窗口,在服务器注销后会直接中断PDF生成流程,而且之前尝试的无头工具因为配置或适配问题没起作用对吧?结合你的场景,我整理了几个针对性的可行方案,从低改动到高稳定性的都有:

方案一:迁移逻辑到Java后端(最优稳定方案)

你的前端JS逻辑其实非常清晰:每隔90秒调用GetInvoicesNo.jsp拉取待处理的发票号,再逐个调用GeneratePDF.jsp生成PDF。这些完全可以在Java后端实现,彻底摆脱浏览器依赖:

  1. 替换前端定时任务:用Java生态的定时任务框架(比如Quartz Scheduler,或者如果是Spring项目直接用Spring Task)替代前端的setInterval,配置每90秒执行一次任务。
  2. 后端模拟HTTP请求
    • HttpURLConnectionRestTemplate或者OkHttp直接调用GetInvoicesNo.jsp的接口,获取发票号列表。
    • 遍历每个发票号,要么直接调用GeneratePDF.jsp对应的Java业务逻辑(效率最高,无需HTTP跳转),要么模拟HTTP请求访问GeneratePDF.jsp生成PDF。
  3. 注册为后台服务:把这个定时任务打包成独立的Java应用(比如Spring Boot Jar),或者用procrun/NSSM注册成Windows服务,完全不受用户登录/注销状态的影响。
  • 优点:彻底脱离浏览器依赖,服务器环境下稳定性拉满,资源占用也远低于浏览器方案;
  • 缺点:需要对现有JSP的Java逻辑有一定了解,做少量代码迁移,不过你的逻辑很简单,改动量其实很小。

方案二:无头浏览器+Windows服务(低改动快速适配方案)

你之前用的是Edge的普通窗口模式,换成无头模式再注册成Windows服务,就能让进程在后台独立运行,不受用户会话影响:

  1. 调整Edge启动命令为无头模式
    msedge --headless=new --disable-gpu --no-sandbox --disable-extensions http://localhost:8088/APP/AutoDownloadPDF.jsp
    
    --headless=new是Edge 90+版本的新无头模式,兼容性更好;如果是旧版本Edge,替换成--headless即可)
  2. 用NSSM包装成Windows服务
    • 下载NSSM(一款轻量的Windows服务管理工具),解压后进入对应系统位数的目录。
    • 执行nssm install AutoPDFGenerator,在弹出的配置窗口中:
      • 「Path」选择Edge的exe完整路径(比如C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe
      • 「Arguments」填入上面的无头模式参数和JSP地址
      • 「Service name」设置为AutoPDFGenerator(自定义名称)
    • 配置完成后,在Windows服务管理器中启动这个服务,它会在系统后台会话运行,即使服务器注销也不会停止。
  • 优点:几乎不需要改动现有JSP/JS代码,5分钟就能完成配置;
  • 缺点:依赖浏览器进程,长期运行可能存在内存泄漏风险,建议定期重启服务,或者在服务配置中设置自动重启。

方案三:重新配置Playwright(针对你之前的尝试)

你提到Playwright没成功,大概率是配置没覆盖到你的JS异步场景(比如没等待AJAX请求完成)。给你一个适配你逻辑的Playwright Java示例,直接模拟你的前端JS行为:

import com.microsoft.playwright.*;
import java.util.concurrent.TimeUnit;

public class AutoPDFGenerator {
    public static void main(String[] args) {
        try (Playwright playwright = Playwright.create()) {
            // 启动Edge无头浏览器
            Browser browser = playwright.chromium().launch(new BrowserType.LaunchOptions()
                    .setHeadless(true)
                    .setChannel("msedge")
                    .setArgs(java.util.Arrays.asList("--no-sandbox", "--disable-gpu")));
            BrowserContext context = browser.newContext();

            while (true) {
                // 模拟前端AJAX请求获取发票号
                Page apiPage = context.newPage();
                String invoiceResponse = apiPage.evaluate("async () => {" +
                        "const res = await fetch('http://localhost:8088/APP/GetInvoicesNo.jsp?EntityID=40&MappingID=<%=getMappingID%>', {method: 'POST'});" +
                        "return await res.text();" +
                        "}");
                apiPage.close();

                String[] invoiceNos = invoiceResponse.trim().split(",");
                for (String invoiceNo : invoiceNos) {
                    if (!invoiceNo.trim().isEmpty()) {
                        String pdfUrl = String.format(
                                "http://localhost:8088/APP/GeneratePDF.jsp?EntityCode=%s&InvoiceNo=%s",
                                "<%=getEntityCode%>", invoiceNo.trim()
                        );
                        // 打开PDF页面生成文件(如果GeneratePDF直接输出流,也可以用page.download()直接保存)
                        Page pdfPage = context.newPage();
                        pdfPage.navigate(pdfUrl);
                        // 等待PDF生成完成,根据实际情况调整等待时间或等待特定元素
                        pdfPage.waitForTimeout(5000);
                        pdfPage.close();
                    }
                }

                // 等待90秒后执行下一轮
                TimeUnit.SECONDS.sleep(90);
            }
        } catch (Exception e) {
            e.printStackTrace();
            // 这里可以加异常重试或日志告警逻辑
        }
    }
}

把这个代码打包成Jar,再用NSSM注册成Windows服务,就能在后台稳定运行了。

  • 优点:可以复用现有JS的逻辑思路,无需大改后端;
  • 缺点:需要编写少量Java代码,调试Playwright的配置可能需要花点时间。

给你之前尝试失败的小提示

如果之前用HTMLUnit没成功,大概率是它对现代JS的支持不足(HTMLUnit对ES6+的兼容性较差);Playwright的话,可能是没指定正确的浏览器渠道、没启用JS支持,或者没等待AJAX请求完成——上面的示例已经帮你覆盖了这些点,可以试试。

备注:内容来源于stack exchange,提问作者rizuwan

火山引擎 最新活动