3

これまでは、Tomcat をフロントサーバーとして持つ JBoss AS 7.1 を使用していました。現在、Tomcat の代替品として undertow が同梱されている Wildfly (JBoss 8.0) にアップグレードしました。

ファイルのダウンロードでは、ファイルの入力ストリームを読み取り、これを外部コンテキストの応答出力ストリームに書き込みます。これは 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ダウンロードを呼び出しており、ストリームをコピーするために次のコードが使用されています。

(スペースを節約するために、try、catch、ログ、およびエラー処理を削除しました)

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()ほかに、書き込み ATTEMPTS を含む、応答ストリームへの追加の書き込みを ommit を呼び出すべきではありませんか?

ドキュメントには次のように書かれています。

この要求に対する HTTP 応答 (HTTP リダイレクトなど) が既に生成されていること、および現在のフェーズが完了したらすぐに要求処理ライフサイクルを終了する必要があることを JavaServer Faces 実装に通知します。

では、応答が送信されたとFaces 実装に通知された場合、なぜ何かを追加しようとするのでしょうか?

于 2014-05-08T13:41:33.893 に答える