在不保存的情况下,在 p:graphicImage 预览中显示 p:fileupload 图像

7

我正在使用PrimeFaces 5.3 <p:fileUpload>上传PNG图片,并希望在将其保存到数据库之前在<p:graphicImage>中显示预览。

这里是一个最小、完整和可复现的示例:

<h:form enctype="multipart/form-data">
    <p:fileUpload value="#{bean.uploadedFile}" mode="simple" />
    <p:graphicImage value="#{bean.image}" />
    <p:commandButton action="#{bean.preview}" ajax="false" value="Preview" />
</h:form>

private UploadedFile uploadedFile;

public UploadedFile getUploadedFile() {
    return uploadedFile;
}

public void setUploadedFile(UploadedFile uploadedFile) {
    this.uploadedFile = uploadedFile;
}

public void preview() {
    // NOOP for now.
}

public StreamedContent getImage() {
    if (uploadedFile == null) {
        return new DefaultStreamedContent(); 
    } else {
        return new DefaultStreamedContent(new ByteArrayInputStream(uploadedFile.getContents()), "image/png"); 
    }
}

后端没有出现任何错误,但是图片在前端不能加载和显示。客户提到该图片返回了404未找到错误。


你说上传功能正常,那么这个问题与 [tag:graphic-image] 无关,而是与 [tag:file-upload] 相关吗? - Kukeltje
是的,正确的。我想这可能是一个“未保存”的上传文件,因为我将其分配给一个临时变量,只有在最终得到用户确认后才会执行永久保存。 - Lance Leroy
那么我必须将文件存储在文件系统或数据库的其他位置吗?没有办法不保存就显示图像吗? - Lance Leroy
当然有,可以在更长的作用域bean中实现...但你想这样做吗? - Kukeltje
1
https://dev59.com/fGw05IYBdhLWcg3wykvn - Kukeltje
显示剩余7条评论
1个回答

13

你的问题有两个方面。它失败是因为上传文件内容是请求范围的,而且图片是在不同的HTTP请求中请求的。为了更好地理解内部工作原理,请仔细阅读以下相关问答中的答案:

要解决第一个问题,您需要在与表单提交相关联的操作方法中立即读取上传的文件内容。在您的特定情况下,代码应该是这样的:

private UploadedFile uploadedFile;
private byte[] fileContents;

public void preview() {
    fileContents = uploadedFile.getContents();
}

// ...
为了解决第二个问题,最好使用数据URI方案。这使得在同一响应中直接渲染图像成为可能,因此您可以安全地使用@ViewScoped bean而不会遇到“上下文未激活”问题或将byte []保存在会话或磁盘中以启用在不同请求中提供图像的功能。数据URI方案在浏览器支持上目前相当不错。请用以下内容替换整个:
<ui:fragment rendered="#{not empty bean.uploadedFile}">
    <img src="data:image/png;base64,#{bean.imageContentsAsBase64}" />
</ui:fragment>

public String getImageContentsAsBase64() {
    return Base64.getEncoder().encodeToString(imageContents);
}
注意:我假设您可以使用Java 8,因为java.util.Base64仅在该版本中引入。如果您使用旧版本的Java,请改用DatatypeConverter.printBase64Binary(imageContents)
如果您恰好使用JSF实用程序库OmniFaces,您还可以直接使用其<o:graphicImage>组件,该组件与<p:graphicImage>相反,能够直接引用byte[]InputStream bean属性并呈现数据URI。
<o:graphicImage value="#{bean.imageContents}" dataURI="true" rendered="#{not empty bean.imageContents}">

感谢 @BalusC,问题现在已经完全验证并根据您的解决方案得到了解决,非常感谢您的努力。 - Lance Leroy
@BalusC,为什么在我的情况下不起作用? 存储库 https://github.com/zaknafein83/uploadimage-primefaces-tutorial.git当我启动时,我会在将byte[]内容转换为String(Base64)的方法上得到一个nullpointer异常。 如果我添加一个空指针控制,我就永远无法获得图像的预览。 - Zaknafein
当用户选择文件时,是否有一种方法可以显示图像? - Hicham

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接