如何从jsf支持bean提供文件下载?

os8fio9y  于 2021-08-25  发布在  Java
关注(0)|答案(4)|浏览(389)

有没有办法从jsf支持bean操作方法提供文件下载?我试过很多东西。主要的问题是我不知道如何获得 OutputStream 以将文件内容写入。我知道如何用一只手做这件事 Servlet ,但这不能从jsf表单调用,需要新的请求。
我怎样才能拿到票 OutputStream 来自当前的响应 FacesContext ?

tjjdgumg

tjjdgumg1#

导言

你可以把一切都搞定 ExternalContext . 在JSF1.x中,您可以获得原始 HttpServletResponse 反对 ExternalContext#getResponse() . 在JSF2.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/不要使用远程命令!

您只需要确保操作方法不是由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 .
e、 g.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();

e、 g.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,那么您可以使用三种方便的方法之一 Faces#sendFile() 方法采用 File ,或 InputStream ,或 byte[] ,并指定是否应将文件作为附件下载( true )或内联( false ).

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

是的,此代码按原样完整。您不需要调用 responseComplete() 你自己也是。此方法还正确处理ie特定的头和utf-8文件名。你可以在这里找到源代码。

xjreopfe

xjreopfe2#

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();

}
6bc51xsx

6bc51xsx3#

这就是我的工作原理:

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();
}
nsc4cvqm

nsc4cvqm4#

下面是完整的代码片段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();
   }
 }

如果希望在运行时生成文件,可以更改文件读取逻辑。

相关问题