【问题标题】:Why does an image in graphicImage not load fully (PrimeFaces mobile)?为什么graphicImage中的图像没有完全加载(PrimeFaces mobile)?
【发布时间】:2014-12-27 15:37:07
【问题描述】:

我有一个应用程序,它使用 PrimeFaces Mobile 来显示图像。

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

带有该图像的页面的 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(03.11.2014 23:21 MSK):

我尝试了以下方法来修复错误:

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

2) 通过将 maxExtensionSizemaxTrailerSize (server.xml) 设置为 -1 来禁用响应分块。

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是图片的网址。

在 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));
    }
}

【问题讨论】:

    标签: jsf jsf-2 primefaces


    【解决方案1】:

    您的readImage() 方法有一个重大错误:

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

    InputStream#available() 不会像您认为的那样做。它不会返回代码其余部分所期望的总内容长度。它返回可用于读取的字节量,而不会阻塞所有其他线程(即当前已放入硬件缓冲区的字节)。这完全解释了为什么您只能显示图像的 部分。

    不必感到羞耻。几乎所有 Java 初学者都使用same mistake。完整读取InputStream 的正确方法是调用any read() 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;
        }
    }
    

    【讨论】:

    • BalusC,你是Java之王!非常感谢(需要等待 12 小时才能获得赏金)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-26
    • 1970-01-01
    相关资源
    最近更新 更多