JSFバッキングBeanアクションメソッドからファイルのダウンロードを提供する方法はありますか?私はたくさんのことを試みました。OutputStream
主な問題は、ファイルの内容を書き込むために応答を取得する方法がわからないことです。でそれを行う方法を知っていますServlet
が、これはJSFフォームから呼び出すことができず、新しいリクエストが必要です。
OutputStream
現在の応答をどのように取得できFacesContext
ますか?
JSFバッキングBeanアクションメソッドからファイルのダウンロードを提供する方法はありますか?私はたくさんのことを試みました。OutputStream
主な問題は、ファイルの内容を書き込むために応答を取得する方法がわからないことです。でそれを行う方法を知っていますServlet
が、これはJSFフォームから呼び出すことができず、新しいリクエストが必要です。
OutputStream
現在の応答をどのように取得できFacesContext
ますか?
あなたはすべてを通り抜けることができますExternalContext
。HttpServletResponse
JSF 1.xでは、。によってrawオブジェクトを取得できますExternalContext#getResponse()
。ExternalContext#getResponseOutputStream()
JSF 2.xでは、JSFの内部から取得する必要なしに、一連の新しいデリゲートメソッドを使用できますHttpServletResponse
。
応答時にContent-Type
、クライアントが提供されたファイルに関連付けるアプリケーションを認識できるようにヘッダーを設定する必要があります。また、クライアントがダウンロードの進行状況を計算できるようにヘッダーを設定する必要がContent-Length
あります。そうしないと、不明になります。また、 [名前を付けて保存]ダイアログが必要な場合は、Content-Disposition
ヘッダーをに設定する必要があります。そうしないと、クライアントはヘッダーをインラインで表示しようとします。最後に、ファイルの内容を応答出力ストリームに書き込むだけです。attachment
最も重要な部分はFacesContext#responseComplete()
、応答にファイルを書き込んだ後、ナビゲーションとレンダリングを実行しないようにJSFに通知することです。そうしないと、応答の最後がページのHTMLコンテンツ、または古いJSFバージョンで汚染されます。 、JSF実装がHTMLをレンダリングするために呼び出すときのIllegalStateException
ようなメッセージが表示されます。getoutputstream() has already been called for this response
getWriter()
アクションメソッドがajaxリクエストによって呼び出されていないことを確認する必要があるだけですが、とを使用して起動する<h:commandLink>
と、通常のリクエストによって呼び出されます<h:commandButton>
。AjaxリクエストとリモートコマンドはJavaScriptによって処理されます。JavaScriptには、セキュリティ上の理由から、ajaxレスポンスのコンテンツとの[名前を付けて保存]ダイアログを強制する機能がありません。
たとえばPrimeFacesを使用している場合は、属性<p:commandXxx>
を介してajaxを明示的にオフにする必要があります。ajax="false"
ICEfacesを使用している場合は<f:ajax disabled="true" />
、コマンドコンポーネントにをネストする必要があります。
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.
}
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
は、使用されているAPIが期待する場所にファイルを提供するだけ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ユーティリティライブラリOmniFacesを使用している場合は、、、、、またはのいずれかを使用して、ファイルを添付ファイル( )またはインライン()としてダウンロードするかどうかを指定する3つの便利Faces#sendFile()
な方法のいずれかを使用できます。File
InputStream
byte[]
true
false
public void download() throws IOException {
Faces.sendFile(file, true);
}
はい、このコードはそのままで完了します。自分で呼び出す必要はありませんresponseComplete()
。このメソッドは、IE固有のヘッダーとUTF-8ファイル名も適切に処理します。ここでソースコードを見つけることができます。
public void download() throws IOException
{
File file = new File("file.txt");
FacesContext facesContext = FacesContext.getCurrentInstance();
HttpServletResponse response =
(HttpServletResponse) facesContext.getExternalContext().getResponse();
response.reset();
response.setHeader("Content-Type", "application/octet-stream");
response.setHeader("Content-Disposition", "attachment;filename=file.txt");
OutputStream responseOutputStream = response.getOutputStream();
InputStream fileInputStream = new FileInputStream(file);
byte[] bytesBuffer = new byte[2048];
int bytesRead;
while ((bytesRead = fileInputStream.read(bytesBuffer)) > 0)
{
responseOutputStream.write(bytesBuffer, 0, bytesRead);
}
responseOutputStream.flush();
fileInputStream.close();
responseOutputStream.close();
facesContext.responseComplete();
}
これは私のために働いたものです:
public void downloadFile(String filename) throws IOException {
final FacesContext fc = FacesContext.getCurrentInstance();
final ExternalContext externalContext = fc.getExternalContext();
final File file = new File(filename);
externalContext.responseReset();
externalContext.setResponseContentType(ContentType.APPLICATION_OCTET_STREAM.getMimeType());
externalContext.setResponseContentLength(Long.valueOf(file.lastModified()).intValue());
externalContext.setResponseHeader("Content-Disposition", "attachment;filename=" + file.getName());
final HttpServletResponse response = (HttpServletResponse) externalContext.getResponse();
FileInputStream input = new FileInputStream(file);
byte[] buffer = new byte[1024];
final ServletOutputStream out = response.getOutputStream();
while ((input.read(buffer)) != -1) {
out.write(buffer);
}
out.flush();
fc.responseComplete();
}
これは私の解決策であり、BalusCの答えの延長です
public static void download(
ByteArrayOutputStream baos,
String downloadFileName,
String contentType
) {
FacesContext context = FacesContext.getCurrentInstance();
ExternalContext externalContext = context.getExternalContext();
externalContext.responseReset();
HttpServletResponse response = (HttpServletResponse) externalContext.getResponse();
response.reset();
response.setContentType(contentType);
response.setHeader("Expires", "0");
response.setHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0");
response.setHeader("Pragma", "public");
Integer size = baos.size();
response.setHeader("Content-Length", size.toString());
response.setHeader(
"Content-Disposition",
"attachment; filename=\"" + downloadFileName + "\""
);
try {
try (OutputStream responseOs = response.getOutputStream()) {
baos.writeTo(responseOs);
}
}
catch (IOException e) {
throw new IOUncheckedException(e);
}
context.responseComplete();
}
これが完全なコードスニペットですhttp://bharatonjava.wordpress.com/2013/02/01/downloading-file-in-jsf-2/
@ManagedBean(name = "formBean")
@SessionScoped
public class FormBean implements Serializable
{
private static final long serialVersionUID = 1L;
/**
* Download file.
*/
public void downloadFile() throws IOException
{
File file = new File("C:\\docs\\instructions.txt");
InputStream fis = new FileInputStream(file);
byte[] buf = new byte[1024];
int offset = 0;
int numRead = 0;
while ((offset < buf.length) && ((numRead = fis.read(buf, offset, buf.length -offset)) >= 0))
{
offset += numRead;
}
fis.close();
HttpServletResponse response =
(HttpServletResponse) FacesContext.getCurrentInstance()
.getExternalContext().getResponse();
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment;filename=instructions.txt");
response.getOutputStream().write(buf);
response.getOutputStream().flush();
response.getOutputStream().close();
FacesContext.getCurrentInstance().responseComplete();
}
}
実行時にファイルを生成する場合は、ファイル読み取りロジックを変更できます。