简体   繁体   English

为什么graphicImage中的图像无法完全加载(PrimeFaces移动版)?

[英]Why does an image in graphicImage not load fully (PrimeFaces mobile)?

I have an application, which uses PrimeFaces Mobile to display images. 我有一个使用PrimeFaces Mobile显示图像的应用程序。

Sometimes, but not always, the image is not displayed fully - only the top part. 有时但并非总是如此,图像无法完全显示-仅顶部显示。

屏幕截图

The XHTML code of the page with that image looks like this: 具有该图像的页面的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>

And the main bean has following code: 并且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));
        }
    }

    [...]
}

How can I fix this error? 如何解决此错误?

Update 1 (03.11.2014 23:21 MSK): 更新1(03.11.2014 23:21 MSK):

I've tried following to fix the error: 我已经尝试按照以下方法修复错误:

1) Disabling cache for all elements of that Primefaces page. 1)为该Primefaces页面的所有元素禁用缓存。

2) Disabling response chunking by setting maxExtensionSize and maxTrailerSize ( server.xml ) to -1 . 2)通过将maxExtensionSizemaxTrailerSizeserver.xml )设置为-1来禁用响应分块。

响应分块设置

3) Adding a filter with following doFilter : 3)使用以下doFilter添加过滤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) Changing the currentImage method to 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);
    }
}

But it still doesn't work. 但这仍然行不通。 I wrote a piece of code in another web application and using different framework (Vaadin), which displays images from the same source. 我在另一个Web应用程序中使用了不同的框架(Vaadin)编写了一段代码,该框架显示来自同一源的图像。

I get the same error (images are displayed only partially). 我收到相同的错误(图像仅部分显示)。

From this I conclude that the error must occur 由此我得出结论,错误必定会发生

  1. when images are retrieved from a particular and/or 从特定的和/或
  2. when images are saved in MongoDB. 将图像保存在MongoDB中时。

Code for retrieving images from URL 用于从URL检索图像的代码

If the error occurs during reading the image, it occurs in the following method: 如果在读取图像期间发生错误,则按以下方法发生:

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 is the URL of the image. viewItemURL是图像的URL。

Code for saving image in MongoDB 在MongoDB中保存图像的代码

If the problem is with saving images in the database, it occurs in the following method: 如果问题在于将图像保存在数据库中,则将通过以下方法发生:

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

Your readImage() method has a major bug: 您的readImage()方法有一个主要错误:

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

The InputStream#available() doesn't do what you think it does. InputStream#available()不会执行您认为的操作。 It doesn't return the total content length which is what the remainder of the code is expecting. 它不会返回总的内容长度,这是其余代码所期望的。 It returns the amount of bytes available for reading without blocking all other threads (ie bytes which are currently already put in hardware buffer). 它返回可读取的字节数,而不会阻塞所有其他线程(即,当前已放入硬件缓冲区的字节)。 This totally explains why you get only that part of the image to display. 这完全解释了为什么只显示图像的一部分。

No need to be ashamed. 无需羞愧。 Practically all Java starters make the same mistake . 实际上,所有Java入门者都会犯同样的错误 The right way to read an InputStream fully is to invoke any read() method on it as long as until it returns -1 indicating EOF (end of file). 完全读取InputStream的正确方法是,对其调用任何 read() 方法 ,直到返回-1表示EOF(文件末尾)为止。 You can find a bunch of examples and utility library shortcuts in this related question: Convert InputStream to byte array in Java . 您可以在以下相关问题中找到大量示例和实用程序库快捷方式: 将InputStream转换为Java中的字节数组

Here's a full rewrite of readImage() method doing the right thing, making use of IOUtils which you appear to already have at your hands (and Java 7's try-with-resources with AutoCloseable ): 下面是一个完整的重写readImage()方法做正确的事,利用IOUtils ,你似乎已经在你的手(和Java 7的尝试-以资源AutoCloseable ):

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM