我使用JSF,并且有一个h:commandButton提示下载文件。该文件为PDF格式。下载的文件具有正确的页数,但它们为空白。

在浏览器中打开文件时,出现以下消息:


  该PDF文档可能无法正确显示。


这是我的commandButton:

    <h:form>
        <h:commandButton action="#{fileDownloadView.fileDownloadView}" value="Download"/>
    </h:form>


这是我的课:

@ManagedBean
public class FileDownloadView {

    private static final String FILENAME = "manual.pdf";
    private static final String CONTENT_TYPE = "application/pdf";

    public FileDownloadView() throws IOException, DocumentException {
        Resource resource = new ClassPathResource(FILENAME);
        InputStream stream = resource.getInputStream();
        FacesContext facesContext = FacesContext.getCurrentInstance();
        ExternalContext externalContext = facesContext.getExternalContext();
        externalContext.responseReset();
        externalContext.setResponseContentType(CONTENT_TYPE);
        externalContext.setResponseHeader("Content-Disposition", "attachment; filename=\"" + FILENAME + "\"");
        OutputStream outputStream = externalContext.getResponseOutputStream();
        byte[] bytes = IOUtils.toByteArray(stream);
        outputStream.write(bytes);
        facesContext.responseComplete();
    }

}


这可能是什么原因?

编辑:

声称重复的帖子给出了以下代码:

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.
}


如果您仔细观察,我的代码是一样的,除了以下注释部分:


  //现在,您可以将文件的InputStream写入上面
  OutputStream的通常方式。
      // ...


我为此写的是

byte[] bytes = IOUtils.toByteArray(stream);
outputStream.write(bytes);


怎么了这不是写在输出流中的字节数组吗?为什么这不起作用?

最佳答案

没错,bean的构造函数的主体与示例中给出的download方法的主体相同。

您的命令链接<h:commandButton action="#{fileDownloadView.fileDownloadView}" .../>操作方法表达式试图在您的bean上查找并调用一个名为fileDownloadView的方法,但是该方法不存在。

您的public FileDownloadView是构造函数,因为它没有返回值类型,并且与该类具有相同的名称。如果将其更改为public void download,则该bean将不再具有显式的构造函数,而最终将具有可由命令按钮调用的方法,如下所示:

 <h:commandButton action="#{fileDownloadView.download}" value="Download"/>


大写很重要,因此请勿调用方法public void Download之类。

我不确定当前实现中实际发生了什么,但是我想在解析#{fileDownloadView.fileDownloadView}的同时,会从表达式的第一部分创建一个新的Bean fileDownloadView实例,并且构造函数中的代码已成功执行。某些CPU容器之后,ELResolver无法解析表达式的第二个.fileDownloadView部分,并引发了异常,使事情变得混乱。

关于java - 在JSF中下载PDF返回空白页,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/54399198/

10-09 09:49