BLOB图像仅在通过p更新后刷新页面时显示:dataTable在PrimeFaces中生成

时间:2014-05-30 04:00:17

标签: jsf tomcat primefaces jsf-2.2 graphicimage

我在<p:graphicImage>上显示以MySQL格式存储的图像,如下所示。

<p:dataTable var="row" value="#{testManagedBean}" lazy="true" editable="true" rows="10">
    <p:column headerText="id">
        <h:outputText value="#{row.brandId}"/>
    </p:column>

    <p:column headerText="Image">
        <p:cellEditor>
            <f:facet name="output">
                <p:graphicImage value="#{brandBean.image}" height="100" width="100">
                    <f:param name="id" value="#{row.brandId}"/>
                </p:graphicImage>
            </f:facet>
            <f:facet name="input">
                <p:graphicImage id="image" value="#{brandBean.image}" height="100" width="100">
                    <f:param name="id" value="#{row.brandId}"/>
                </p:graphicImage>
            </f:facet>
        </p:cellEditor>
    </p:column>

    <p:column headerText="Edit" width="50">
        <p:rowEditor/>
    </p:column>
</p:dataTable>

编辑行时,<p:fileUpload>上会显示<p:overlayPanel>。为简单起见,本例中省略了这个和许多其他内容,因为它们与具体问题无关。

关联的JSF托管bean:

@ManagedBean
@ViewScoped
public final class TestManagedBean extends LazyDataModel<Brand> implements Serializable
{
    @EJB
    private final TestBeanLocal service=null;
    private static final long serialVersionUID = 1L;

    @Override
    public List<Brand> load(int first, int pageSize, String sortField, SortOrder sortOrder, Map<String, Object> filters) {
        setRowCount(3);
        return service.getList();
    }
}

根据唯一的行标识符BrandBean从数据库中检索图像的bean。

@ManagedBean
@ApplicationScoped
public final class BrandBean
{
    @EJB
    private final BrandBeanLocal service=null;
    public BrandBean() {}

    public StreamedContent getImage() throws IOException {
        FacesContext context = FacesContext.getCurrentInstance();

        if (context.getCurrentPhaseId() == PhaseId.RENDER_RESPONSE) {
            return new DefaultStreamedContent();
        }
        else {
            String id = context.getExternalContext().getRequestParameterMap().get("id");
            System.out.println("id = "+id);
            byte[] bytes = service.findImageById(Long.parseLong(id));
            return bytes==null? new DefaultStreamedContent(new ByteArrayInputStream(new byte[0])):new DefaultStreamedContent(new ByteArrayInputStream(bytes));
        }
    }
}

通过单击数据表最后一列中的定位符(由<p:rowEditor>表示)更新行(在编辑之后),getImage()方法BrandBean }应该按原样调用。

使用PrimeFaces 5.0和JSF 2.2.6在GlassFish Server 4.0上运行的应用程序中正确发生这种情况。

在数据表中更新行后(或因此在数据库中),新数据将立即显示在数据表中。


使用Spring 4.0.0 GA在Tomcat服务器8.0.5上运行另一个应用程序,其中在更新数据表所持有的行之后 getImage()方法未被调用仍然在数据表中显示旧映像(而不是新更新的映像)(即使更改已正确传播到数据库)。

仅当通过按 F5 (在大多数浏览器上)刷新页面时,才会显示新更新的图像。它甚至不会在页面加载时显示(在地址栏中输入URL,然后按输入键)。

换句话说,当通过单击<p:rowEditor>指示的刻度更新数据表中的行时,不会调用getImage()方法(因此,不会从数据库中提取新图像)显示在<p:graphicImage>)上。仅当通过按 F5 快捷键刷新/重新加载页面时才会调用此方法。


为什么会这样?如何在更新行后立即显示新更新的图像?

表面上看,这既不应该与Spring也不应该与JPA相关(点击勾选后更新操作会正确地传播到数据库)。这应该与Tomcat服务器相关。

2 个答案:

答案 0 :(得分:1)

  

仅当通过按F5(在大多数浏览器上)刷新页面时,才会显示新更新的图像。它甚至不会显示在页面加载上(在地址栏中输入URL然后按回车键)。

Webbrowser正在缓存图像。通过响应头中设置的缓存相关指令,基于每个URL缓存资源。导致您的具体问题是因为资源URL仍然相同,并且Web浏览器不知道资源在服务器端已更改。 OmniFaces CacheControlFilter showcase page详细解释了缓存(注意:过滤器不是解决此问题的方法)。

您基本上需要强制Web浏览器通过更改URL来重新请求资源。对于这种情况最常见的方法之一是,可缓存资源突然改变并且其更改需要立即反映到所有客户端,将图像的“最后修改”时间戳附加到URL的查询字符串。鉴于您正在使用JPA,所以应该这样做:

  • lastModified表中添加brand列:

    ALTER TABLE brand ADD COLUMN lastModified TIMESTAMP DEFAULT now();
    
  • 使用适当的属性扩展Brand实体,并设置@PreUpdate设置它:

    @Column @Temporal(TemporalType.TIMESTAMP)
    private Date lastModified;
    
    @PreUpdate
    public void onUpdate() {
        lastModified = new Date();
    }
    
    // +getter+setter
    

    (JPA在每次@PreUpdate查询之前调用UPDATE带注释的方法

  • 将其附加到图片网址(参数名称v是对“版本”的提示):

    <p:graphicImage value="#{brandBean.image}" ...>
        <f:param name="id" value="#{row.brandId}" />
        <f:param name="v" value="#{row.lastModified.time}" />
    </p:graphicImage>
    

    (为了清楚起见,我会将row重命名为brand,将brandId重命名为id以进行重复数据删除)

  • 最后,如果您使用的是PrimeFaces 5.0或更高版本,那么您还需要禁用服务器端缓存:

    <p:graphicImage value="#{brandBean.image}" cache="false" ...>
    

设计通知:如果图片未必在Brand的每次更新时更新,请将其拆分为另一个表Image并让Brand具有FK(@ManyToOne }或@OneToOne)到Image。这也使得属性“图像”可以在webapp中的各种实体之间重用。

答案 1 :(得分:0)

HY,

我有同样的问题,但我的图像src是硬盘而不是DB。 当我使用随机数但我不读它时,我有结果问题。 一般来说,当这个参数是一个随机数时,我在src图像中添加了一个参数。

我的解决方案是这样的:

public class UserPage {
  private String fotoCVPath;
  //all variabile
    private String getRandomImageName() {
        int i = (int) (Math.random() * 10000000);

        return String.valueOf(i);
    }
 public void editImage() {
//init the fotoCVPath
fotoCVPath = fotoCVPath + "?tmpVal=" + getRandomImageName();
}
  /**
     * @return the fotoCVPath <br>
     * 
     * @author  - asghaier <br>
     * 
     *         Created on 29/mag/2014
     */
    public String getFotoCVPath() {

        return fotoCVPath;
    }

    /**
     * @param fotoCVPath the fotoCVPath to set <br>
     * 
     * @author- asghaier <br>
     * 
     *         Created on 29/mag/2014
     */
    public void setFotoCVPath(String fotoCVPath) {
        this.fotoCVPath = fotoCVPath;
    }
}

在我的页面中我有像这样的元素graphicImage:

<h:panelGrid ...al param >
    <h:column>
       <p:graphicImage id="imgCV" value="#{userPage.fotoCVPath}" styleClass="imgCVStyle" />
    </h:column>
</h:panelGrid>

当我喜欢重新加载图片时,更新组件panelGrid。

对于我的例子我已经使用

<p:ajax event="click" onstart="PF('uploadImgCV').show();" update ="idPanelGrid"/>

in

我希望这种模式可以帮助您解决问题