7

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

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

编辑行时,a<p:fileUpload>上会显示 a <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();
    }
}

基于唯一行标识符从数据库中检索图像的 bean - BrandBean.

@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,然后按enter键)。

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


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

从表面上看,这应该与 Spring 和 JPA 无关(点击勾选后更新操作正确传播到数据库)。这应该与Tomcat服务器有关。

4

2 回答 2

1

The newly updated image is displayed only when the page refreshed by pressing F5 (on most browsers). It is even not displayed on page load (entering a URL into the address bar and then pressing the enter key).

The image is being cached by the webbrowser. Resources are cached on a per-URL basis via the cache-related instructions set in the response headers. Your concrete problem is caused because the resource URL is still the same and the webbrowser isn't aware that the resource has changed in the server side. The OmniFaces CacheControlFilter showcase page explains caching in detail (note: the filter is not the solution to this problem).

You basically need to force the webbrowser to re-request the resource by changing the URL. One of most common approaches for this kind of situation, whereby a cacheable resource is suddenly changed and its change needs to be immediately reflected to all clients, is appending the "last modified" timestamp of the image to the query string of the URL. Given that you're using JPA, so this should do:

  • Add a lastModified column to the brand table:

    ALTER TABLE brand ADD COLUMN lastModified TIMESTAMP DEFAULT now();
    
  • Extend the Brand entity with the appropriate property and a @PreUpdate which sets it:

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

    (a @PreUpdate annotated method is invoked by JPA right before every UPDATE query)

  • Append it to the image URL (the parameter name v is a hint to "version"):

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

    (I would here rename row to brand for clarity and brandId to id to deduplicate)

  • Finally, if you're using PrimeFaces 5.0 or newer, then you also need to disable server side caching:

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

Design notice: if the image is not necessarily updated on every update of Brand, then split it off to another table Image and let Brand have a FK (@ManyToOne or @OneToOne) to Image. This also makes the property "image" reusable across various entities in the webapp.

于 2014-06-01T16:36:31.123 回答
0

嘿,

我有同样的问题,但我的图像 src 在硬盘中而不是在数据库中。当我使用随机数但我没有阅读它时,我遇到了问题。一般来说,当这个参数是一个随机数时,我会在 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;
    }
}

在你我的页面中,我有这样的元素 graphicsImage:

<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"/>

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

于 2014-06-03T09:18:54.060 回答