1

私の主な質問は、JSF 2とCDIを使用し、ブックマーク可能なURLを使用してバイナリファイル(PDF、ドキュメントなど)を提供するための「グッドプラクティス」はありますか?

JSF 2仕様(JSR 314)を読みましたが、「リソース処理」の段落が存在することがわかりました。ただし、warファイルまたはjarファイルに配置された静的ファイルを提供するためにのみ使用されているようです。特定のResourceHandlerを登録することによってここで対話する方法が存在するかどうかは本当にわかりませんでした...

実際、私はSeamの2つの方法に慣れていました。それは、メソッドを使用してAbstractResourceクラスを拡張し、 URLプレフィックスの後に提供するパスを宣言し、ファイルでを宣言することです。getResource(HttpServletRequest, HttpServletResponse)getResourcePath()<webapp>/seam/resource/SeamResourceServletweb.xml

これが私がしたことです。

私は最初に、JSF 2.0を使用してデータベースに保存されているファイルをダウンロードする方法を見て、それを実装しようとしました。

<f:view ...

    <f:metadata>
        <f:viewParam name="key" value="#{containerAction.key}"/>
        <f:event listener="#{containerAction.preRenderView}" type="preRenderComponent" />
    </f:metadata>

    ...

    <rich:dataGrid columns="1" value="#{containerAction.container.files}" var="file">
        <rich:panel>
                <h:panelGrid columns="2">
                    <h:outputText value="File Name:" />
                    <h:outputText value="#{file.name}" />
                </h:panelGrid>
                <h:form>
                    <h:commandButton value="Download" action="#{containerAction.download(file.key)}" />
                </h:form>
        </rich:panel>
    </rich:dataGrid>

そしてここに豆があります:

@Named
@SessionScoped
public class ContainerAction {

    private Container container;

    /// Injections
    @Inject @DefaultServiceInstance
    private Instance<ContainerService> containerService;

    /// Control methods
    public void preRenderView(final ComponentSystemEvent event) {
        container = containerService.get().loadFromKey(key);
    }

    /// Action methods
    public void download(final String key) throws IOException {
        final FacesContext facesContext = FacesContext.getCurrentInstance();

        HttpServletResponse response = (HttpServletResponse) facesContext.getExternalContext().getResponse();

        final ContainerFile containerFile = containerService.get().loadFromKey(key);
        final InputStream containerFileStream = containerService.get().read(containerFile);

        response.setHeader("Content-Disposition", "attachment;filename=\""+containerFile.getName()+"\"");
        response.setContentType(containerFile.getContentType());
        response.setContentLength((int) containerFile.getSize());

        IOUtils.copy(containerFileStream, response.getOutputStream());

        response.flushBuffer();

        facesContext.responseComplete();
    }

    /// Getters / setters
    public Container getContainer() {
        return container;
    }
}

ここでは、そのEL式を正しく解釈するために、Tomcat 7(6を使用していました)に切り替える必要がありました。動作し@SessionScopedましたが、動作しませんでした@RequestScoped(ボタンをクリックしても何も起こりませんでした)。

しかし、ボタンの代わりにリンクを使用したかったのです。

試し<h:commandLink value="Download" action="#{containerAction.download(file.key)}" />ましたが、醜いjavascriptリンクが生成されます(ブックマークできません)。

JSF 2の仕様を読むと、「ブックマーク機能」機能があるように見えますが、その使用方法は明確ではありません。

実際には、ビューでのみ機能するように見えるので、空のビューを作成してh:link:を作成しました。

<h:link outcome="download.xhtml" value="Download">
    <f:param name="key" value="#{file.key}"/>
</h:link>
<f:view ...>
    <f:metadata>
        <f:viewParam name="key" value="#{containerFileDownloadAction.key}"/>
        <f:event listener="#{containerFileDownloadAction.download}" type="preRenderComponent" />
    </f:metadata>
</f:view>
@Named
@RequestScoped
public class ContainerFileDownloadAction {

    private String key;

    @Inject @DefaultServiceInstance
    private Instance<ContainerService> containerService;

    public void download() throws IOException {
        final FacesContext facesContext = FacesContext.getCurrentInstance();

        // same code as previously
        ...

        facesContext.responseComplete();
    }


    /// getter / setter for key
    ...
}

しかし、その後、私は持っていましたjava.lang.IllegalStateException: "getWriter()" has already been called for this response

ビューが開始するときのロジックは、getWritterを使用して応答を初期化します。

そこで、作業を行うサーブレットを作成し、以下を作成しましたh:outputLink

<h:outputLink value="#{facesContext.externalContext.request.contextPath}/download/">
    <h:outputText value="Download"/>
    <f:param name="key" value="#{file.key}"/>
</h:outputLink>

しかし、その最後の手法でファイルのブックマーク可能なURLが得られたとしても、実際には「JFS2」ではありません...

アドバイスはありますか?

4

3 に答える 3

2

BalusCに同意します。通常、アプリケーションは純粋なJSFアプリケーションではなく、JavaEEアプリケーションです。

JavaEEでhttpリクエストを処理するためにJSFビュー以外のものが存在することは何の意味もありません。Java EE 6では、名前付きCDI Beanは、JAX-RSを使用してパスに直接マップすることもできます。これは、サーブレットを使用する代わりの方法です。その場合、@ Producesと@Pathを使用します(たとえば、JERSEYを使用した入力および出力バイナリストリームを参照してください)。

一方、<f:viewParam>JSFの利点の1つは、バリデーターを簡単にアタッチできることです。現時点では、サーブレットもJAX-RSリソースもそれをサポートしていません。

<h:link><h:outputLink value="#{facesContext.externalContext.request.contextPath}/...">また、いつも書くよりも使いやすいです。ただし、これは、このパーツをFaceletsタグまたは複合コンポーネントでラップすることで軽減できます。

(仕様の将来のバージョンで、JAX-RSリソースに直接リンクするためのリンクタグがJSFに提供されれば素晴らしいと思います(リンクが合法であることを確認するためのオプションのコンテナー起動検証を使用))。

于 2011-06-12T21:10:05.817 に答える
1

JSFは当初から、RESTファイルサービスではなく、MVCフレームワークとして設計されています。

サーブレットはその仕事に完全に適しています。@WebServletJava EE 6のフィーリングを向上させるために、注釈を付けます。

于 2011-06-08T20:38:50.220 に答える
0

実際、PrettyFaces URLRewriteFilter- > http://ocpsoft.org/prettyfaces/serving-dynamic-file-content-with-prettyfaces/を使用すると、この問題を直接解決できます。

このブログでは、まったく新しいMVCフレームワークを使用せずに、やりたいことを正確に実行する方法について説明しています。

于 2012-03-30T21:36:53.537 に答える