[解決済み] JSFバッキングBeanからファイルのダウンロードを提供する方法は?
質問
JSFのバッキングビーンのアクションメソッドからファイルのダウンロードを提供する方法はありますか?
私は多くのことを試しました。主な問題は、私がどのように
OutputStream
にファイルの内容を書き込むために、応答のServlet
で行う方法を知っていますが、これはJSFフォームから呼び出すことができず、新しいリクエストを必要とします。
どうすれば
OutputStream
からレスポンスの
FacesContext
?
どのように解決するのですか?
はじめに
あなたは、すべてのものを
ExternalContext
. JSF 1.xでは、生の
HttpServletResponse
オブジェクトを
ExternalContext#getResponse()
. JSF 2.xでは、以下のような新しいデリゲートメソッドの束を使うことができます。
ExternalContext#getResponseOutputStream()
のような新しいデリゲートメソッドを使うことができます。
HttpServletResponse
を取得することなく、JSFのフードの下にある
レスポンス上では
Content-Type
ヘッダを設定し、クライアントがどのアプリケーションが提供されたファイルに関連づけられるかを知ることができるようにします。そして
Content-Length
ヘッダを設定し、クライアントがダウンロードの進捗を計算できるようにします(そうしないと、不明になってしまいます)。そして
Content-Disposition
ヘッダを
attachment
が必要な場合は
として保存
ダイアログが必要な場合、そうでなければ、クライアントはそれをインラインで表示しようとします。最後に、ファイルの内容をレスポンス出力ストリームに書き込むだけです。
最も重要な部分は
FacesContext#responseComplete()
を呼び出して、JSFに、レスポンスにファイルを書き込んだ後はナビゲーションとレンダリングを実行しないように通知します。そうしないと、レスポンスの最後がページのHTMLコンテンツで汚染されるか、古いJSFのバージョンでは
IllegalStateException
のようなメッセージとともに
getoutputstream() has already been called for this response
を呼び出すと、JSF の実装は
getWriter()
を呼び出してHTMLをレンダリングします。
ajaxをオフにする/リモートコマンドを使用しない!
あなたはただ、アクションメソッドが
ではなく
によって呼び出されるのではなく、通常のリクエストによって呼び出されることを確認するだけです。
<h:commandLink>
と
<h:commandButton>
. Ajax リクエストとリモートコマンドは JavaScript で処理され、JavaScript にはセキュリティ上の理由から、強制的に
として保存
ダイアログを表示させる機能がありません。
PrimeFacesなどを使っている場合は
<p:commandXxx>
を使用している場合、以下のように明示的に ajax をオフにする必要があります。
ajax="false"
属性で明示的にオフにする必要があります。ICEfaces を使用している場合は、ICEfaces のネストとして
<f:ajax disabled="true" />
をコマンドコンポーネントの中に入れ子にする必要があります。
一般的なJSF 2.xの例
public void download() throws IOException {
FacesContext fc = FacesContext.getCurrentInstance();
ExternalContext ec = fc.getExternalContext();
ec.responseReset(); // Some JSF component library or some Filter might have set some headers in the buffer beforehand. We want to get rid of them, else it may collide.
ec.setResponseContentType(contentType); // Check http://www.iana.org/assignments/media-types for all types. Use if necessary ExternalContext#getMimeType() for auto-detection based on filename.
ec.setResponseContentLength(contentLength); // Set it with the file size. This header is optional. It will work if it's omitted, but the download progress will be unknown.
ec.setResponseHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\""); // The Save As popup magic is done here. You can give it any file name you want, this only won't work in MSIE, it will use current request URL as file name instead.
OutputStream output = ec.getResponseOutputStream();
// Now you can write the InputStream of the file to the above OutputStream the usual way.
// ...
fc.responseComplete(); // Important! Otherwise JSF will attempt to render the response which obviously will fail since it's already written with a file and closed.
}
一般的なJSF 1.xの例
public void download() throws IOException {
FacesContext fc = FacesContext.getCurrentInstance();
HttpServletResponse response = (HttpServletResponse) fc.getExternalContext().getResponse();
response.reset(); // Some JSF component library or some Filter might have set some headers in the buffer beforehand. We want to get rid of them, else it may collide.
response.setContentType(contentType); // Check http://www.iana.org/assignments/media-types for all types. Use if necessary ServletContext#getMimeType() for auto-detection based on filename.
response.setContentLength(contentLength); // Set it with the file size. This header is optional. It will work if it's omitted, but the download progress will be unknown.
response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\""); // The Save As popup magic is done here. You can give it any file name you want, this only won't work in MSIE, it will use current request URL as file name instead.
OutputStream output = response.getOutputStream();
// Now you can write the InputStream of the file to the above OutputStream the usual way.
// ...
fc.responseComplete(); // Important! Otherwise JSF will attempt to render the response which obviously will fail since it's already written with a file and closed.
}
一般的な静的ファイルの例
ローカルディスクのファイルシステムから静的ファイルをストリームする必要がある場合は、以下のようなコードに置き換えてください。
File file = new File("/path/to/file.ext");
String fileName = file.getName();
String contentType = ec.getMimeType(fileName); // JSF 1.x: ((ServletContext) ec.getContext()).getMimeType(fileName);
int contentLength = (int) file.length();
// ...
Files.copy(file.toPath(), output);
一般的な動的ファイルの例
PDF や XLS のような動的に生成されるファイルをストリームする必要がある場合、単に
output
を指定します。
OutputStream
.
例:iText PDF。
String fileName = "dynamic.pdf";
String contentType = "application/pdf";
// ...
Document document = new Document();
PdfWriter writer = PdfWriter.getInstance(document, output);
document.open();
// Build PDF content here.
document.close();
例:Apache POI HSSF。
String fileName = "dynamic.xls";
String contentType = "application/vnd.ms-excel";
// ...
HSSFWorkbook workbook = new HSSFWorkbook();
// Build XLS content here.
workbook.write(output);
workbook.close();
ここでコンテンツの長さを設定することはできないことに注意してください。そのため、レスポンスコンテンツの長さを設定するための行を削除する必要があります。これは技術的には問題ありませんが、唯一の欠点は、エンドユーザーが不明なダウンロードの進捗を提示されることです。これが重要な場合、前の章で示したように、最初にローカル(一時)ファイルに書き込んで、それを提供することが本当に必要です。
ユーティリティ メソッド
JSFのユーティリティ・ライブラリである
オムニフェーズ
を使用しているのであれば、3つの便利な
Faces#sendFile()
メソッドを使うことができます。
File
または
InputStream
または
byte[]
で、添付ファイルとしてダウンロードするかどうかを指定する (
true
) か、インライン (
false
).
public void download() throws IOException {
Faces.sendFile(file, true);
}
はい、このコードはそのままで完全です。を呼び出す必要はありません。
responseComplete()
などを自分で呼び出す必要はありません。この方法は、IE特有のヘッダやUTF-8のファイル名にも適切に対応します。を見つけることができます。
ソースコードはこちら
.
関連
-
[解決済み] java.lang.ClassNotFoundException: javax.faces.webapp.FacesServlet
-
[解決済み] Androidでファイルをダウンロードし、ProgressDialogで進捗を表示する。
-
[解決済み] GitHubから単一ファイルをダウンロードする
-
[解決済み] ファイルをダウンロードし、別のファイル名で保存するwgetコマンド
-
[解決済み】ASP.NET MVCでファイルを表示/ダウンロードに戻す場合
-
[解決済み】JSFリソースライブラリは何のためにあり、どのように使用すべきですか?
-
[解決済み】JSF 2.0 Faceletsを使用してXHTMLに別のXHTMLを含めるにはどうすればよいですか?
-
[解決済み】PrimeFacesのprocess/updateとJSFのf:ajax execute/renderの属性を理解する。
-
[解決済み] Java EE / JSF で j_security_check を使用してユーザー認証を行う。
-
[解決済み] divタグをレンダリングできるjsfコンポーネントは何ですか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】PrimeFacesのprocess/updateとJSFのf:ajax execute/renderの属性を理解する。
-
[解決済み] JSF2 FaceletsでJSTL...意味があるのか?
-
[解決済み] Java EE / JSF で j_security_check を使用してユーザー認証を行う。
-
[解決済み] <f:metadata>、<f:viewParam>、<f:viewAction>は何に使えるのでしょうか?
-
[解決済み] javax.el.PropertyNotFoundException の識別と解決。ターゲットに到達できない
-
[解決済み] h:commandLinkの代わりにh:outputLinkを使うべきですか?
-
[解決済み] PrimeFaces p:fileUploadを使うには?リスナーメソッドが呼び出されない、またはUploadedFileがNullである / エラーが発生する / 使えない
-
[解決済み] サーブレット関連クラスでJSFマネージドBeanを名前付きで取得する
-
[解決済み] dataTableやui:repeatのコマンドリンクに選択した行を渡すにはどうしたらいいですか?
-
[解決済み] ViewParam vs @ManagedProperty(value = "#{param.id}")