【问题标题】:Display uploaded images before store to disk在存储到磁盘之前显示上传的图像
【发布时间】:2013-12-18 11:23:08
【问题描述】:

大家好,

我正在使用 primefaces 4 和 Tomcat 7。我希望用户能够上传多个图像并在将这些图像写入磁盘之前立即查看每个上传的图像(当它们在内存中时)。图像只会在表单提交后写入磁盘。我正在使用 p:fileUpload 组件。

以下是相关代码:

...
<p:tab id="imageTab" title="#{msgs.images}">

    <p:dataGrid id="imagesDataGrid" columns="4" value="#{modifyProductAdminBean.imageIds}" 
                                    var="imgId" >
        <p:graphicImage value="#{pA_ImageService.image}" >
            <f:param name="id" value="#{imgId}" />
        </p:graphicImage>    
    </p:dataGrid>

    <p:fileUpload fileUploadListener="#{modifyProductAdminBean.handleFileUpload}" mode="advanced"
        dragDropSupport="true" multiple="true" sizeLimit="5242880"
        invalidFileMessage="#{msgs.invalidFileType}"
        invalidSizeMessage="#{msgs.fileTooLarge}"
        allowTypes="/(\.|\/)(gif|jpe?g|png|jpg)$/"
        cancelLabel="#{msgs.cancel}"
        uploadLabel="#{msgs.upload}"
        label="#{msgs.choose}"
        update="imagesDataGrid" />
</p:tab>
...


@ManagedBean
@ViewScoped
public class ModifyProductAdminBean implements Serializable {
    private Map<String, UploadedFile> uploadedImages;

    public void handleFileUpload(FileUploadEvent event) {
        UploadedFile file = event.getFile(); 
        String uniqueId = UUID.randomUUID().toString();
        this.getUploadedImages().put(uniqueId, file);
    }

    public Set<String> getImageIds() {
        return this.getUploadedImages().keySet();
    }

    public Map<String, UploadedFile> getUploadedImages() {
        return uploadedImages;
    }
    ...
}


@ManagedBean
@ApplicationScoped
public class PA_ImageService implements Serializable {

    private final ModifyProductAdminBean modifyProductAdminBean;

    public PA_ImageService() {
        this.modifyProductAdminBean = BeanManager.findBean("modifyProductAdminBean");
    }

    // Taken from http://stackoverflow.com/questions/8207325/display-image-from-database-with-pgraphicimage
    public StreamedContent getImage() {
        FacesContext context = FacesContext.getCurrentInstance();

        if (context.getCurrentPhaseId() == PhaseId.RENDER_RESPONSE) {
            // So, we're rendering the HTML. Return a stub StreamedContent so that it will generate right URL.
            return new DefaultStreamedContent();
        } else {
            // So, browser is requesting the image. Return a real StreamedContent with the image bytes.
            String imageId = context.getExternalContext().getRequestParameterMap().get("id");
            // remove [, ] characters between
            imageId = imageId.substring(1, imageId.length() - 1);
            UploadedFile uFile = this.modifyProductAdminBean.getUploadedImages().get(imageId);
            return new DefaultStreamedContent(new ByteArrayInputStream(uFile.getContents()));
        }
    }

    ...
}


public class BeanManager implements Serializable {
    @SuppressWarnings("unchecked")
    public static <T> T findBean(String beanName) {
        FacesContext context = FacesContext.getCurrentInstance();
        return (T) context.getApplication().evaluateExpressionGet(context, "#{" + beanName + "}", Object.class);
    }
    ...
}

当我运行此代码时,我在“PA_ImageService”的最后一行收到 NullPointerException(返回新的 ...)。更准确地说,虽然 uFile 不为 null,但“uFile.getContents()”返回 null。为什么?我做错了什么?


我观察到的更多细节:

我注意到,当我上传文件时,Tomcat 将其临时存储在 E:\Program Files (x86)\Apache Software Foundation\Apache Tomcat 7.0.41\work\Catalina\localhost\MyProject 目录中的 .tmp 文件中。

通过调试项目,我可以看到:当我到达PA_ImageServiceif (context... == PhaseId.RENDER_RESPONSE)行时,.tmp文件仍然存在。但是,在getImage()方法的第二次访问中,当控件移动到else块时,我可以看到tmp文件不再存在。因此,无法检索其内容,因此无法检索 null 结果。

对这是如何发生的有任何想法吗?

【问题讨论】:

  • 我在这个问题的末尾添加了额外的观察。请看一下,如果你能帮上忙,请告诉我!谢谢

标签: jsf file-upload primefaces


【解决方案1】:

您需要将图像存储在(临时)磁盘/数据库位置,而不是作为视图范围 bean 的属性。您也许可以将其存储为会话范围 bean 的属性,但我不建议在 HTTP 会话中携带内存消耗字节,这在需要序列化时会受到伤害,例如服务器集群。

您可以轻松地使用File#renameTo() 从临时位置移动到固定位置。您可以轻松运行会话侦听器来获取/清理任何与用户相关的临时文件。

【讨论】:

  • 感谢您的回复。我正在使用 Netbeans,当我调试项目时,我看到 this.modifyProductAdminBean = BeanManager.findBean("modifyProductAdminBean") 返回一个与预期视图范围 bean 具有完全相同的值的 bean。这向我表明它返回了我想要的预期视图范围 bean。如果我错了,请纠正我。
  • 对,如果在同一个视图中第一次创建应用程序范围的 bean,它会起作用,但它肯定不适用于其他/后续视图,因为已经创建了应用程序范围的 bean .我从答案中删除了不再相关的部分。
  • 另外,我刚刚在question 中找到了您的答案,您还建议使用bytes[],但在这里您不建议这样做吗?
  • 我不建议将它存储在会话中(作为会话范围的 bean 属性,理论上是另一种解决方案)。就是这样。
  • 好的,非常感谢。我将更改我的代码以将其存储在临时位置。
【解决方案2】:

这里最明显的问题是您试图从@ApplicationScoped bean 中访问@ViewScoped bean;这在 JSF 中是非法的。

您只能注入比注入目标范围更广的 bean。这意味着您可以按以下范围顺序注入 bean:

ApplicationScope >> SessionScope >> ViewScope >> RequestScope

话虽如此,虽然我看不到您如何将 ModifyProductAdminBean 注入 PA_ImageService(没有注释或 faces-config.xml 可见),但可以肯定地说以下行不应该工作

UploadedFile uFile = this.modifyProductAdminBean.getUploadedImages().get(imageId);

【讨论】:

  • 我已经更新了我的问题以显示我如何注入 ModifyProductAdminBean。我按照here 的建议制作了PA_ImageService 一个ApplicationScoped bean。这段代码有效,因为我只在使用ModifyProductAdminBean 的.xhtml 文件中访问PA_ImageService。所以ModifyProductAdminBean 总是存在的。正如我在问题中所说,uFile 已成功检索。问题是uFile.getContents() 返回null。为什么uFilenull 内容?
猜你喜欢
  • 2012-07-29
  • 2018-12-22
  • 2011-08-06
  • 2011-08-25
  • 1970-01-01
  • 2011-10-04
  • 1970-01-01
  • 2012-01-19
  • 1970-01-01
相关资源
最近更新 更多