在JSF中下载PDF会返回空白页面



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

当文件在浏览器中打开时,我会收到以下消息:

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

这是我的命令按钮:

<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写入上面的以通常的方式输出流。//。。。

我为此写的是

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

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

您是对的,您的bean的构造函数的主体与示例中给出的下载方法的主体相同。

命令链接<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}的同时,从表达式的第一部分创建了一个新的beanfileDownloadView实例,构造函数中的代码也成功执行了。一些CPU循环之后,ELResolver无法解析表达式的第二个.fileDownloadView部分,并抛出一个异常,这有点把事情搞砸了。

最新更新