3

到目前为止,我们使用的是 JBoss AS 7.1,它有一个 tomcat 作为前端服务器。我们现在升级到 Wildfly (JBoss 8.0),它附带 undertow 作为 tomcat 的替代品。

对于我们的文件下载,我们正在读取文件的输入流,并将其写入外部上下文的响应输出流。这在 JBoss AS 7.1 中运行良好 - 即使对于大文件也是如此。在 Undertow 中,即使对于非常“小”的文件,我们也会收到以下异常:

13:04:43,292 ERROR [io.undertow.request] (default task-15) Blocking request failed HttpServerExchange{ GET /project/getFile.xhtml}: java.lang.RuntimeException: org.xnio.channels.FixedLengthOverflowException
    at io.undertow.servlet.spec.HttpServletResponseImpl.responseDone(HttpServletResponseImpl.java:527)
    at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:287)
    at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:227)
    at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:73)
    at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:146)
    at io.undertow.server.Connectors.executeRootHandler(Connectors.java:168)
    at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:687)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) [rt.jar:1.7.0_51]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) [rt.jar:1.7.0_51]
    at java.lang.Thread.run(Thread.java:744) [rt.jar:1.7.0_51]
Caused by: org.xnio.channels.FixedLengthOverflowException
    at io.undertow.conduits.AbstractFixedLengthStreamSinkConduit.write(AbstractFixedLengthStreamSinkConduit.java:97)
    at org.xnio.conduits.Conduits.writeFinalBasic(Conduits.java:132) [xnio-api-3.2.0.Final.jar:3.2.0.Final]
    at io.undertow.conduits.AbstractFixedLengthStreamSinkConduit.writeFinal(AbstractFixedLengthStreamSinkConduit.java:137)
    at org.xnio.conduits.ConduitStreamSinkChannel.writeFinal(ConduitStreamSinkChannel.java:104) [xnio-api-3.2.0.Final.jar:3.2.0.Final]
    at io.undertow.channels.DetachableStreamSinkChannel.writeFinal(DetachableStreamSinkChannel.java:172)
    at io.undertow.servlet.spec.ServletOutputStreamImpl.writeBufferBlocking(ServletOutputStreamImpl.java:580)
    at io.undertow.servlet.spec.ServletOutputStreamImpl.close(ServletOutputStreamImpl.java:614)
    at io.undertow.servlet.spec.HttpServletResponseImpl.closeStreamAndWriter(HttpServletResponseImpl.java:451)
    at io.undertow.servlet.spec.HttpServletResponseImpl.responseDone(HttpServletResponseImpl.java:525)
    ... 9 more

正在调用下载,以下getFile.xhtml代码用于复制流:

(删除了尝试、捕获、日志和错误处理以节省一些空间)

public void downloadFile(FileEntity fileEntity) {
        FacesContext fc = FacesContext.getCurrentInstance();
        ExternalContext ec = fc.getExternalContext();

        ec.responseReset();
        ec.setResponseContentType(getMimeType(fileEntity.getFile()));
        ec.setResponseContentLength(new Long(fileEntity.getFile().length()).intValue());
        ec.setResponseHeader("Content-Disposition", "attachment; filename=\"" + ConversionHelper.validateFilename(fileEntity.getDisplayFileName()) + "\"");

        OutputStream output = ec.getResponseOutputStream();
        FileInputStream fis = new FileInputStream(fileEntity.getFile());

        IOUtils.copy(fis, output);

        fc.responseComplete();
    }

我注意到删除线

ec.setResponseContentLength(new Long(fileEntity.getFile().length()).intValue());

让它再次工作。但是 ResponseConentLength 是“正确的”。使用

ec.setResponseContentLength(new Long(fileEntity.getFile().length()).intValue() + 9);

(注意+9)解决了这个问题。使用+8-> FixedLengthOverflow,使用+10->FixedLengthUnderflow

+9文件大小无关。任何想法?在我看来很奇怪...

4

2 回答 2

2

如果你调用 output.close() 它应该有你想要的效果。这看起来像是 JSF 的问题,与 Undertow 无关。

于 2014-05-09T03:55:10.520 回答
1

发现了问题。

它与服务器引擎的更改有关。只是不确定它是否与undertow直接相关,或者更多是wildfly / JSF的错误(或者它是否是Jboss中的错误并且现在可以正常工作):

为了调用下载,我们在 getFile.xhtml 中使用了类似的内容:

    <f:metadata>
        <f:viewParam name="fileId" required="true"
            value="#{getFileController.fileId}"></f:viewParam>
    </f:metadata>

    <h:outputLabel value="#{getFileController.download()}" />

这通常会产生:

<label>value</label>

如果标签的值是一个字符串。

由于我们重置了内部的响应download()<label>因此再次被删除。然后,使用等于文件长度的固定响应大小并调用responseComplete()基本上省略了尾随</label>. 然而,Undertow 似乎忽略了responseComplete()-Call 并尝试附加</label>到响应中,注意到已到达(固定)响应流的末尾,因此引发了上述异常。

这就是为什么提供大小+9可以解决此错误 - 但会导致文件损坏,原因</label>\n将附加到文件中。

显然,使用输出标签是不好的做法。但是由于我们重置并手动填充响应流并调用responseComplete()它工作正常。

修复显然是不在该页面上生成任何(不需要)html标签:

    <f:metadata>
        <f:viewParam name="fileId" required="true"
            value="#{getFileController.fileId}"></f:viewParam>
        <f:event listener="#{getFileController.dlNow()}" type="preRenderView"></f:event>
    </f:metadata>

除了糟糕的设计 - 不应该调用responseComplete()ommit 对响应流的任何额外写入,包括任何写入尝试?

文档说:

通知 JavaServer Faces 实现,该请求的 HTTP 响应已经生成(例如 HTTP 重定向),并且请求处理生命周期应在当前阶段完成后立即终止。

所以,如果 Faces 实现被告知响应已经发送 - 为什么它会尝试附加一些东西?

于 2014-05-08T13:41:33.893 回答