92

JSFバッキングBeanアクションメソッドからファイルのダウンロードを提供する方法はありますか?私はたくさんのことを試みました。OutputStream主な問題は、ファイルの内容を書き込むために応答を取得する方法がわからないことです。でそれを行う方法を知っていますServletが、これはJSFフォームから呼び出すことができず、新しいリクエストが必要です。

OutputStream現在の応答をどのように取得できFacesContextますか?

4

5 に答える 5

250

序章

あなたはすべてを通り抜けることができますExternalContextHttpServletResponseJSF 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 responsegetWriter()

ajaxをオフにする/リモートコマンドを使用しないでください!

アクションメソッドがajaxリクエストによって呼び出されていないことを確認する必要があるだけですが、とを使用して起動する<h:commandLink>と、通常のリクエストによって呼び出されます<h:commandButton>。AjaxリクエストとリモートコマンドはJavaScriptによって処理されます。JavaScriptには、セキュリティ上の理由から、ajaxレスポンスのコンテンツとの[名前を付けて保存]ダイアログを強制する機能がありません。

たとえばPrimeFacesを使用している場合は、属性<p:commandXxx>を介してajaxを明示的にオフにする必要があります。ajax="false"ICEfacesを使用している場合は<f:ajax disabled="true" />、コマンドコンポーネントにをネストする必要があります。

一般的なJSF2.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.
}

一般的なJSF1.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は、使用されている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()な方法のいずれかを使用できます。FileInputStreambyte[]truefalse

public void download() throws IOException {
    Faces.sendFile(file, true);
}

はい、このコードはそのままで完了します。自分で呼び出す必要はありませんresponseComplete()。このメソッドは、IE固有のヘッダーとUTF-8ファイル名も適切に処理します。ここでソースコードを見つけることができます。

于 2012-02-22T11:52:31.907 に答える
5
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();

}
于 2016-10-03T10:01:56.923 に答える
3

これは私のために働いたものです:

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();
}
于 2017-04-28T09:06:43.973 に答える
0

これは私の解決策であり、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();
}
于 2021-08-10T13:06:59.993 に答える
-3

これが完全なコードスニペットです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();
   }
 }

実行時にファイルを生成する場合は、ファイル読み取りロジックを変更できます。

于 2013-02-05T10:57:14.447 に答える