1. ホーム
  2. jsf

[解決済み] PrimeFaces p:fileUploadを使うには?リスナーメソッドが呼び出されない、またはUploadedFileがNullである / エラーが発生する / 使えない

2022-11-06 02:25:57

質問内容

PrimeFacesを使ってファイルをアップロードしようとしましたが fileUploadListener メソッドはアップロード終了後に呼び出されません。

以下はそのビューです。

<h:form>
    <p:fileUpload fileUploadListener="#{fileUploadController.handleFileUpload}"
        mode="advanced" 
        update="messages"
        sizeLimit="100000" 
        allowTypes="/(\.|\/)(gif|jpe?g|png)$/"/>

    <p:growl id="messages" showDetail="true"/>
</h:form>

そして、ビーン。

@ManagedBean
@RequestScoped
public class FileUploadController {

    public void handleFileUpload(FileUploadEvent event) {
        FacesMessage msg = new FacesMessage("Succesful", event.getFile().getFileName() + " is uploaded.");
        FacesContext.getCurrentInstance().addMessage(null, msg);
    }

}

メソッドにブレークポイントを置いたのですが、一向に呼び出されません。を使用する場合 mode="simple"ajax="false" で、呼び出されるのですが、アドバンスドモードで動作させたいのです。私はNetbeansとGlassfish 3.1を使っています。

どのように解決するのですか?

設定方法とトラブルシューティング <p:fileUpload> は、PrimeFacesとJSFのバージョンに依存します。

すべてのPrimeFacesのバージョン

以下の要件は、すべてのPrimeFacesのバージョンに適用されます。

  1. 以下の enctype の属性は <h:form> を設定する必要があります。 multipart/form-data . これがない場合、ajaxによるアップロードはうまくいくかもしれませんが、一般的なブラウザの動作は、フォームの構成やウェブブラウザのメーカー/バージョンに依存し、不特定多数になります。念のため、必ず指定してください。

  2. を使用する場合 mode="advanced" (すなわち ajax アップロード、これはデフォルトです) を使用する場合は、必ず <h:head> を (マスター) テンプレートに追加してください。これにより、必要な JavaScript ファイルが適切にインクルードされます。の場合は必要ありません。 mode="simple" (非ajaxアップロード)では必要ありませんが、これは他のすべてのPrimeFacesコンポーネントのルックアンドフィールと機能を壊すことになるので、いずれにせよ見逃したくはないでしょう。

  3. を使用する場合 mode="simple" (すなわち非ajaxアップロード)を使用している場合、PrimeFacesコマンドボタン/リンクのajaxを以下のように無効化する必要があります。 ajax="false" によって、PrimeFacesのコマンドボタンやリンクでjaxを無効にしなければなりません。 <p:fileUpload value> と共に <p:commandButton action> の代わりに <p:fileUpload listener> .

ですから、もしあなたがajaxサポートで(自動)ファイルアップロードをしたいのであれば( <h:head> !):

<h:form enctype="multipart/form-data">
    <p:fileUpload listener="#{bean.upload}" auto="true" /> // For PrimeFaces version older than 8.x this should be fileUploadListener instead of listener.
</h:form>

public void upload(FileUploadEvent event) {
    UploadedFile uploadedFile = event.getFile();
    String fileName = uploadedFile.getFileName();
    String contentType = uploadedFile.getContentType();
    byte[] contents = uploadedFile.getContents(); // Or getInputStream()
    // ... Save it, now!
}

また、ajax以外のファイルアップロードが必要な場合。

<h:form enctype="multipart/form-data">
    <p:fileUpload mode="simple" value="#{bean.uploadedFile}" />
    <p:commandButton value="Upload" action="#{bean.upload}" ajax="false" />
</h:form>

private transient UploadedFile uploadedFile; // +getter+setter

public void upload() {
    String fileName = uploadedFile.getFileName();
    String contentType = uploadedFile.getContentType();
    byte[] contents = uploadedFile.getContents(); // Or getInputStream()
    // ... Save it, now!
}

のようなajaxに関連する属性があることに注意してください。 auto , allowTypes , update , onstart , oncomplete などは は無視されます。 mode="simple" . なので、このような場合は指定する必要はありません。

また、注意点として ファイルの中身をすぐに読む を行う必要があることに注意してください。これはアップロードされたファイルの内容がリクエストスコープされているため、後の/異なるHTTPリクエストで利用できないからです。後のリクエストでそれを読み込もうとすると、ほとんどの場合 java.io.FileNotFoundException になってしまいます。


PrimeFaces 8.x

設定は以下の5.xバージョンの情報と同じですが、リスナーが呼び出されない場合は、method属性が呼び出されているかどうかを確認します。 listener となっているか、また、(8.x以前のバージョンと同じように)メソッド属性が fileUploadListener .


PrimeFaces 5.x

これは ではなく は、JSF 2.2 を使っていて、かつ faces-config.xml が JSF 2.2 バージョンに適合していることを宣言している場合。あなたは ではなく はPrimeFacesのファイルアップロードフィルタを全く必要とせず、さらに ではなく が必要です。 primefaces.UPLOADER のコンテキストパラメータが必要です。 web.xml . 使用するターゲットサーバーに応じてJSFを適切にインストールし、設定する方法が不明な場合は、次のサイトにアクセスしてください。 Mavenを使用してJSFライブラリを適切にインストールおよび設定する方法は? および JSF wikiページの "JSFのインストール"のセクションを参照してください。 .

しかし、JSF 2.2をまだ使用しておらず、アップグレードできない場合(Servlet 3.0互換コンテナ上では楽なはずです)、以下のPrimeFacesファイルアップロードフィルタを手動で web.xml (これはマルチパートリクエストをパースし、通常のリクエストパラメータマップを埋めるので FacesServlet が通常通り動作するようにします)。

<filter>
    <filter-name>primeFacesFileUploadFilter</filter-name>
    <filter-class>org.primefaces.webapp.filter.FileUploadFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>primeFacesFileUploadFilter</filter-name>
    <servlet-name>facesServlet</servlet-name>
</filter-mapping>

<servlet-name> の値は facesServlet の値と正確に一致しなければなりません。 <servlet> のエントリーは javax.faces.webapp.FacesServlet の中で、同じ web.xml . ですから、もしそれが例えば Faces Servlet であれば、それに合うように編集する必要があります。


PrimeFaces 4.x

PrimeFaces 5.xと同じ話が4.xにも当てはまります。

によってアップロードされたファイルの内容を取得する際に、潜在的な問題があるだけです。 UploadedFile#getContents() . これは null のように、Apache Commons FileUpload ではなく、ネイティブ API を使用した場合です。この場合 UploadedFile#getInputStream() を使う必要があります。また p:fileUploadからアップロードされた画像をMySQLのBLOBとして挿入するにはどうすればよいですか?

ネイティブ API の別の潜在的な問題は、アップロード コンポーネントを処理しない別の "regular" ajax リクエストが発生するフォームにアップロード コンポーネントが存在する場合に発生する可能性があります。以下も参照してください。 PrimeFaces 4.0/JSF 2.2.xのAJAXでファイルアップロードが動作しない - javax.servlet.ServletException: リクエストのコンテントタイプが multipart/form-data ではありません。 .

どちらの問題も、Apache Commons FileUploadに切り替えることで解決することもできます。詳しくはPrimeFaces 3.xの項を参照してください。


PrimeFaces 3.x

このバージョンでは、JSF 2.2 / Servlet 3.0 のネイティブファイルアップロードはサポートされていません。Apache Commons FileUploadを手動でインストールし、ファイルアップロードフィルタを明示的に web.xml .

以下のライブラリが必要です。

これらはウェブアプリケーションのランタイムクラスパスに存在しなければなりません。Maven を使用する場合、これらが少なくとも実行時のスコープであることを確認してください (デフォルトのコンパイルのスコープもよいです)。手動でJARを持ち歩く場合、それらが最終的に /WEB-INF/lib フォルダに収まるようにします。

ファイルアップロードフィルタの登録の詳細は、上記のPrimeFaces 5.xのセクションに記載されています。PrimeFaces 4+を使用していて、JSF 2.2 / Servlet 3.0のネイティブファイルアップロードの代わりにApache Commons FileUploadを明示的に使用したい場合は、上記のライブラリとフィルターの他に、以下のコンテキストパラメーターが必要です。 web.xml :

<context-param>
    <param-name>primefaces.UPLOADER</param-name>
    <param-value>commons</param-value><!-- Allowed values: auto, native and commons. -->
</context-param>


トラブルシューティング

それでもうまくいかない場合、PrimeFacesの設定とは関係なく考えられる別の原因を紹介します。

  1. PrimeFacesのファイルアップロードフィルタを使用している場合のみです。もうひとつの Filter を実行するウェブアプリケーションで の前に を実行するWebアプリケーションで、すでにリクエストボディを消費しています。 getParameter() , getParameterMap() , getReader() などです。リクエストボディは一度だけ解析することができます。ファイルアップロードフィルタが仕事をする前に、これらのメソッドの1つを呼び出すと、ファイルアップロードフィルタは空のリクエストボディを取得することになります。

    これを修正するためには、ファイルアップロードフィルタに <filter-mapping> を、ファイルアップロードフィルタの の前に にある他のフィルタの web.xml . もしリクエストが multipart/form-data リクエストでない場合、ファイルアップロードフィルタは何も起きなかったかのように処理を続けます。アノテーションを使用しているために自動的に追加されるフィルタ (例: PrettyFaces) を使用している場合は、web.xml で明示的に順序を追加する必要があるかもしれません。以下を参照してください。 WAR でアノテーションを使用してサーブレットフィルタの実行順序を定義する方法

  2. PrimeFacesのファイルアップロードフィルタを使用している場合のみです。もうひとつの Filter を実行するウェブアプリケーションで の前に の前に実行され、PrimeFacesのファイルアップロードフィルタで RequestDispatcher#forward() を呼び出します。通常、URL リライトフィルタのような プリティフェイス がこれを行います。これがトリガーとなって FORWARD ディスパッチャを起動しますが、フィルタはデフォルトで REQUEST ディスパッチャのみを listen します。

    これを修正するためには、PrimeFacesのファイルアップロードフィルタに の前に に置くか、PrimeFacesのファイルアップロードフィルタを再設定して FORWARD ディスパッチャもリッスンするようにPrimeFacesのファイルアップロードフィルタを再構成します。

     <filter-mapping>
         <filter-name>primeFacesFileUploadFilter</filter-name>
         <servlet-name>facesServlet</servlet-name>
         <dispatcher>REQUEST</dispatcher>
         <dispatcher>FORWARD</dispatcher>
     </filter-mapping>
    
    
  3. ネストされた <h:form> . これはHTMLでは違法であり、ブラウザの動作は不定です。多くの場合、ブラウザは送信時に期待されたデータを送信しません。を入れ子にしていないことを確認してください。 <h:form> . これは完全にフォームの enctype . ただ、フォームをネストすることは一切ありません。

まだ問題がある場合は、まあ、HTTP トラフィックをデバッグしてください。ウェブブラウザの開発者ツールセット (Chrome/Firebug23+/IE9+ では F12 を押す) を開き、「Net/Network」セクションを確認します。HTTPの部分が問題なさそうなら、JSFのコードをデバッグしてください。ブレークポイントを FileUploadRenderer#decode() にブレークポイントを置き、そこから先に進みます。


アップロードしたファイルの保存

ようやく動作するようになったところで、次の質問はおそらく、「アップロードしたファイルをどこにどのように保存すればよいのですか?では、ここから先をご覧ください。 JSFでアップロードされたファイルを保存する方法 .