10

实际情况是这样的:Java Web 服务器(Weblogic)收到用户的请求,它必须发送一个 ZIP 存档作为响应。存档必须从用户要求的一些文件和服务器自身生成的一份 HTML 报告中动态生成。我想重用服务器在其他情况下已经使用的 JSF servlet 来生成此报告。所以,基本上,我使用的是:

HttpURLConnection  self = new URL ("http://me.myself.com/report.jsf?...").openConnection ();
String  report_html = fetchHtmlFromConnection (self);

然后创建请求的 ZIP,包括其中生成的 HTML。

问题是,在这种情况下,我能否以某种方式避免发出内部 HTTP 请求(to report.jsf)?这基本上涉及到通过操作系统、HTTPD(可能在不同的机器上)等的往返(因为应用程序只是与自己“对话”)。

4

5 回答 5

2

我对 JSF 不是很熟悉,但是根据我对它们的了解,您可以使用一种也适用于 JSP 页面的技术:

  • 创建自己的HttpServletResponseWrapper(容器使用的类,可让您修改响应)
  • 使用它来覆盖默认值Writer(将呈现的页面写入输出)并提供一个将输出写入到String将提供压缩代码的临时文件或临时文件。

有一个漂亮而简单的教程向您展示如何做到这一点: http: //blog.valotas.com/2011/09/get-output-of-jsp-or-servlet-response.html

然后

  • 正如 gyan 所暗示的,ServletRequestDispatcher从您的 servlet 中获取一个,它可以让您调用 JSF 的呈现
  • 转发 servlet 调用以提供您自己的HttpServletResponseWrapper
  • 使用 yourHttpServletResponseWrapper获取呈现的 HTML 并将其提供给邮政编码。

所以压缩 Servlet 就像:

TempFileRespWrapper respWrapper = new TempFileRespWrapper();
RequestDispatcher dispatcher = getServletContext().getRequestDispatcher( "/report.jsf");
dispatcher.forward(request, respWrapper);
File f = respWrapper.getOutputPath();
addFileToZip(f);
于 2014-02-24T06:11:37.443 回答
2

您应该有一个业务服务层,以便可以在多个演示视图(甚至是“zip 文件”视图)中使用“generateReport”服务。

您可以通过 EJB 或通过任何允许您指定可注入业务服务(例如 spring)的自定义框架以标准 J2EE 方式执行此操作。

我认为这里的主要问题是您只能通过客户端界面(http)生成报告。这使它成为一个独立的服务,对应用程序的其他部分不可用。基本上你需要重构。

不要在 JSF 或类似文件中编写业务代码。(顺便说一句,尽量不要使用 jsf :D)


  BUSINESS LAYER              PRESENTATION
generateReportService---|---jsf-HtmlReport
                     \__|___
                        |   \ 
someOtherContentService-|----jsf-Zip
于 2014-02-21T16:26:10.603 回答
1

考虑一下请求调度策略,其中请求/响应对象将从入口 servlet 发送到报告 servlet。反过来,报告 servlet 将生成报告,并且可以将控制发送到下一个 servlet,从而完成其余的压缩和发送过程。

对于构造 RequestDispatcher 对象,您可以使用ServletRequest.getRequestDispatcher()方法或ServletContext.getRequestDispatcher()方法。

RequestDispatcher dispatcher=getServletContext().getRequestDispatcher( "/report.jsf" );
dispatcher.forward( request, response );

下一个 servlet 将 mime 类型设置为 'application/zip' 并将 zip 二进制文件写入浏览器。用户的浏览器将根据浏览器设置以下载形式处理内容。

于 2013-10-09T09:39:11.017 回答
0

测试解决方案!

这个解决方案实际上得到了已经在这里发布的想法(特别是来自@gyan)。

  1. 编写一个 Servlet 来压缩

(您也可以为此使用过滤器。例如,假设您有一个 ZipFilter。您可以映射所有 *.zip 的过滤器,并将该过滤器链接到相应的 .jsf URL 的 URLRewrite 过滤器)。

public class ZipServlet
    extends HttpServlet {

    @Override
    public void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException,
            IOException {

        ZipEntry zipEntry = new ZipEntry("helloWorld.html");
        ZipHttpServletResponseWrapper respWrapper = new ZipHttpServletResponseWrapper(response, zipEntry);
        RequestDispatcher dispatcher = getServletContext().getRequestDispatcher("/helloWorld.jsf");
        dispatcher.forward(request, respWrapper);
        response.setContentType("application/zip");
        response.setHeader("Content-Disposition", "inline; filename=output.zip;");
        response.flushBuffer();
        respWrapper.getOutputStream().close();

    }

}
  • 注意:是的,您应该使用 RequestDispatcher

    1. ZipHttpServletResponseWrapper

没什么好说的。从 Java 7 开始,您可以使用本机类来正确创建 zip 文件。应该推荐在 response.getOutputStream() 之上使用带有 ZipOutputStream 的装饰器模式。

请记住,HttpServletResponseWrapper 是一个装饰器。如果您不想直接重用目标“servlet”输出(您可以使用特技 HttpServletResponse 而不是使用 HttpServletResponseWrapper),则不应使用它。

public class ZipHttpServletResponseWrapper
    extends HttpServletResponseWrapper {

    private ZipEntry entry;
    private ZipServletOutputStreamWrapper streamWrapper;
    private ZipOutputStream outputStream;
    private PrintWriter printWriter;

    public ZipHttpServletResponseWrapper(HttpServletResponse response, ZipEntry entry) {
        super(response);
        this.entry = entry;
    }

    @Override
    public ServletOutputStream getOutputStream() throws IOException {
        if (streamWrapper == null) {
            outputStream = new ZipOutputStream(this.getResponse().getOutputStream());
            outputStream.putNextEntry(entry);
            streamWrapper = new ZipServletOutputStreamWrapper(outputStream);
        }
        return streamWrapper;
    }

    @Override
    public PrintWriter getWriter() throws IOException {
        if (printWriter == null) {
            printWriter = new PrintWriter(getOutputStream());
        }
        return printWriter;
    }

    private class ZipServletOutputStreamWrapper
        extends ServletOutputStream {

        private ZipOutputStream outputStream;

        public ZipServletOutputStreamWrapper(ZipOutputStream outputStream) {
            this.outputStream = outputStream;
        }

        @Override
        public void close() throws IOException {
            outputStream.closeEntry();
            outputStream.finish();
        }

        @Override
        public void write(int b) throws IOException {
            outputStream.write(b);
        }
    }

}
  1. 现在,秘密:明智地映射!

JSF 的一些重要部分可以使用过滤器链(例如,Apache 的 myfaces 使用一些扩展来提供一些 JSF 组件)。在这种情况下,您应该在这些过滤器中指定摘要 FORWARD 和 REQUEST

    <filter-mapping>
        <filter-name>extensionsFilter</filter-name>
        <url-pattern>*.jsf</url-pattern>
        <dispatcher>REQUEST</dispatcher>
        <dispatcher>FORWARD</dispatcher>    
    </filter-mapping>
于 2014-02-28T18:45:11.520 回答
0

确保您的 Web 服务器已配置为缓存(generated)html,您不必担心它。对完整或部分 URL 的第一次请求将生成对 jsf 生成器的调用,然后将从 Web 服务器缓存中获取。

是的,会涉及一些开销

(您的来源 -> 我们的服务器 -> 生成器页面或缓存)

但最终会变得更容易。

于 2014-02-27T16:50:54.827 回答