2014-10-31 67 views
1

我有一个应用程序,它使用PrimeFaces Mobile来显示图像。为什么graphicImage中的图像未完全加载(PrimeFaces mobile)?

有时候,但并非总是如此,图像不能完全显示 - 只有顶部。

Screenshot

与图像页面的XHTML代码如下所示:

<!DOCTYPE html> 
<html xmlns="http://www.w3.org/1999/xhtml" 
     xmlns:h="http://java.sun.com/jsf/html" 
     xmlns:f="http://java.sun.com/jsf/core" 
     xmlns:p="http://primefaces.org/ui" 
     xmlns:pm="http://primefaces.org/mobile"> 

<f:view renderKitId="PRIMEFACES_MOBILE"/> 

<h:head> 

</h:head> 

<f:event listener="#{main.loadFirstImage}" type="preRenderView" /> 

<h:body id="body"> 

    <pm:page id="page"> 
     <pm:header title="myapp"> 
     </pm:header> 

     <pm:content id="content"> 
      <h:form> 
       <p:graphicImage id="image" rendered="false" value="#{main.currentImage()}" 
           cache="false"> 
       </p:graphicImage> 

       [...] 

      </h:form> 
     </pm:content> 

     <pm:footer title="m.myapp.com"></pm:footer> 
    </pm:page> 
</h:body> 

</html> 

而且main bean有以下代码:

@ManagedBean(name = "main") 
@SessionScoped 
public class MainView { 

    private byte[] currentImageData; 
    private byte[] productId; 
    private byte[] imageId; 

    public void loadFirstImage() 
    { 
     // This method initializes currentImageData 
     fetchNextImage(); 
    } 

    [...] 

    public StreamedContent currentImage() 
    { 
     FacesContext context = FacesContext.getCurrentInstance(); 

     if (context.getCurrentPhaseId() == PhaseId.RENDER_RESPONSE) { 
      return new DefaultStreamedContent(); 
     } 
     else { 
      return new DefaultStreamedContent(new ByteArrayInputStream(currentImageData)); 
     } 
    } 

    [...] 
} 

我怎样才能解决这个错误?

更新1(2014年3月11日23:21 MSK):

我试过以下修正这个错误:

1)禁用缓存为Primefaces页面中的所有元素。

2)通过将maxExtensionSizemaxTrailerSizeserver.xml)设置为-1来禁用响应块。

Response chunking settings

3)添加过滤器具有以下doFilter

@Override 
public void doFilter(final ServletRequest aServletRequest, 
        final ServletResponse aServletResponse, 
        final FilterChain aFilterChain) throws IOException, ServletException { 
    System.out.println("aServletRequest instanceof HttpServletRequest: " + 
      (aServletRequest instanceof HttpServletRequest)); 

    if (aServletRequest instanceof HttpServletRequest) 
    { 
     final HttpServletRequest request = (HttpServletRequest) aServletRequest; 

     final String requestURI = request.getRequestURI().toLowerCase(); 

     if (!requestURI.endsWith("/javax.faces.resource/dynamiccontent.properties")) 
     { 
      aFilterChain.doFilter(aServletRequest, aServletResponse); 
     } 
    } 
} 

4)改变currentImage方法

public StreamedContent currentImage() 
{ 
    FacesContext context = FacesContext.getCurrentInstance(); 

    if (context.getCurrentPhaseId() == PhaseId.RENDER_RESPONSE) { 
     // So, we're rendering the view. Return a stub StreamedContent so that it will generate right URL. 
     return new DefaultStreamedContent(); 
    } 
    else { 
     String mimeType = null; 

     if (imageFileName.toLowerCase().endsWith(".png")) 
     { 
      mimeType = "image/png"; 
     } 
     else if (imageFileName.toLowerCase().endsWith(".jpeg") || imageFileName.toLowerCase().endsWith(".jpg")) 
     { 
      mimeType = "image/jpeg"; 
     } 

     // So, browser is requesting the image. Return a real StreamedContent with the image bytes. 
     return new DefaultStreamedContent(new ByteArrayInputStream(currentImageData), mimeType); 
    } 
} 

但它仍然无法正常工作。我在另一个Web应用程序中编写了一段代码,并使用不同的框架(Vaadin),它显示来自同一个源的图像。

我得到相同的错误(图像只显示部分)。

从这我的结论是必须发生错误

  1. 当图像被从一个特定的和/或
  2. 当图像被保存在MongoDB中检索。

代码读取图像期间从URL

如果发生错误检索图像,它发生在下面的方法:

protected Binary readImage(final String viewItemURL) { 
    InputStream inputStream = null; 
    Binary image = null; 
    try 
    { 
     inputStream = new URL(viewItemURL).openStream();; 
     byte bytes[] = new byte[inputStream.available()]; 
     inputStream.read(bytes); 

     image = new Binary(bytes); 
    } 
    catch (final IOException exception) 
    { 
     LOGGER.error("", exception); 
    } 
    finally 
    { 
     IOUtils.closeQuietly(inputStream); 
    } 
    return image; 
} 

viewItemURL是图像的URL。

代码如果问题是与数据库中的图像保存在MongoDB中保存图像

,它发生在下面的方法:

protected void saveProductImages(final byte[] aNewProductId, final List<String> aPictureUrls, 
           final IMongoPersistenceState aPersistenceState) { 
    final DB db = aPersistenceState.getDb(); 
    final DBCollection productImagesColl = db.getCollection(
      MyAppPersistenceAction.COLLECTION_USER_PRODUCT_IMAGES); 

    for (final String curPictureUrl : aPictureUrls) 
    { 
     final Binary imageData = readImage(curPictureUrl); 

     final Map<String,Object> map = new HashMap<String, Object>(); 

     map.put(FIELD_COLLECTION_USER_PRODUCT_IMAGES_CREATOR_EMAIL, CREATOR_EMAIL); 
     map.put(FIELD_COLLECTION_USER_PRODUCT_IMAGES_PRODUCT_ID, aNewProductId); 
     map.put(FIELD_COLLECTION_USER_PRODUCT_IMAGES_DATA, imageData); 
     final String fileName = extractFileName(curPictureUrl); 
     map.put(FIELD_COLLECTION_USER_PRODUCT_IMAGES_FILE_NAME, fileName); 
     map.put(FIELD_COLLECTION_USER_PRODUCT_IMAGES_MIME_TYPE, getMimeType(fileName)); 
     map.put(FIELD_COLLECTION_USER_PRODUCT_IMAGES_IS_DELETED, Boolean.FALSE); 

     productImagesColl.insert(WriteConcern.SAFE, createRecordObject(map)); 
    } 
} 

回答

4

readImage()方法有一个重大缺陷:

byte bytes[] = new byte[inputStream.available()]; 

InputStream#available()不符合您的想法。它不会返回代码的其余部分所期望的总内容长度。它返回可用于读取的字节数量,而不会阻塞所有其他线程(即当前已存放在硬件缓冲区中的字节)。这完全解释了为什么你只能得到部分图片才能显示。

不需要感到羞耻。几乎所有Java初学者都使用same mistake。完全读取InputStream的正确方法是调用anyread()method,直到它返回-1指示EOF(文件结束)。您可以在这个相关问题中找到一堆示例和实用程序库快捷方式:Convert InputStream to byte array in Java

这里是readImage()方法的全部重写做正确的事,利用IOUtils,你似乎已经在你的手(和Java 7的try-with-resourcesAutoCloseable):

protected Binary readImage(final String viewItemURL) { 
    try (InputStream inputStream = new URL(viewItemURL).openStream()) { 
     return new Binary(IOUtils.toByteArray(inputStream)); 
    } 
    catch (final IOException exception) { 
     LOGGER.error("", exception); 
     return null; 
    } 
} 
+0

BalusC,你是国王Java的!非常感谢(需要等待12小时才能授予奖励)。 – 2014-11-04 08:38:36