Отображение динамического изображения из базы данных с помощью p: graphicImage и StreamedContent - PullRequest
46 голосов
/ 21 ноября 2011

Я пытаюсь отобразить байты изображения, которые сохраняются в базе данных как StreamedContent в <p:graphicImage>, следующим образом:

<p:graphicImage  value="#{item.imageF}" width="50"  id="grpImage" height="80"/>
private StreamedContent content; // getter and setter

public StreamedContent getImageF() {

    if (student.getImage() != null) {
        InputStream is = new ByteArrayInputStream(student.getImage());
        System.out.println("Byte :"+student.getImage());
        content = new DefaultStreamedContent(is, "", student.getStuID());
        System.out.println("ddd ------------------------------- " + content);
        return content;
    }

    return content;
}

Возвращает пустое изображение. Как это вызвано и как я могу решить это?

В стандартный вывод выводится следующее:

INFO: Byte :[B@a2fb48
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@b0887b
INFO: Byte :[B@a2fb48
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@1d06a92
INFO: Byte :[B@d52f0b
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@39a60
INFO: Byte :[B@d52f0b
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@8c3daa
INFO: Byte :[B@124728a
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@1dbe05b
INFO: Byte :[B@124728a
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@66a266
INFO: Byte :[B@a2fb48
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@1293976
INFO: Byte :[B@a2fb48
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@17b7399
INFO: Byte :[B@d52f0b
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@1e245a5
INFO: Byte :[B@d52f0b
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@4a7153
INFO: Byte :[B@124728a
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@1561bfd
INFO: Byte :[B@124728a
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@47a8c2

Ответы [ 4 ]

91 голосов
/ 17 сентября 2012

<p:graphicImage> требует специального метода получения. Именно он будет вызываться дважды для каждого сгенерированного изображения, каждое в совершенно другом HTTP-запросе.

Первый HTTP-запрос, который запросил HTML-результат страницы JSF, впервые вызовет метод получения, чтобы сгенерировать элемент HTML <img> с правильным уникальным и автоматически сгенерированным URL-адресом в * 1005. * атрибут, который содержит информацию о том, какой именно бин и получатель должны быть вызваны всякий раз, когда веб-браузер собирается запросить изображение. Обратите внимание, что геттер в данный момент не должен возвращать содержимое изображения. Он не будет использоваться никоим образом, так как HTML не работает (изображения не «встроены» в вывод HTML, но вместо этого они запрашиваются отдельно).

Как только веб-браузер извлекает результат HTML как ответ HTTP, он анализирует источник HTML, чтобы визуально представить результат конечному пользователю. Когда веб-браузер обнаруживает элемент <img> во время синтаксического анализа источника HTML, он отправляет новый HTTP-запрос по URL-адресу, указанному в его атрибуте src, чтобы загрузить содержимое этого изображения и внедрить его в визуальный элемент. презентация. Это вызовет метод получения во второй раз, который в свою очередь должен вернуть фактическое содержимое изображения.

В ваш конкретный случай PrimeFaces, по-видимому, либо не смог идентифицировать и вызвать геттер для извлечения фактического содержимого изображения, либо получатель не возвратил ожидаемый контент изображения. Использование #{item} имени переменной и количества вызовов в журнале предполагает, что вы использовали ее в <ui:repeat> или <h:dataTable>. Скорее всего, базовый компонент имеет область запроса, и модель данных не сохраняется должным образом во время запроса изображения, и JSF не сможет вызвать геттер во время правильного цикла итерации. Бин с областью видимости также не будет работать, поскольку состояние просмотра JSF нигде не доступно, когда браузер фактически запрашивает изображение.


Чтобы решить эту проблему, лучше всего переписать метод получения как таковой, чтобы его можно было вызывать для каждого запроса, в котором вы передаете уникальный идентификатор изображения как <f:param> вместо того, чтобы полагаться на какой-либо компонент поддержки. свойства, которые могут выйти из синхронизации во время последующих HTTP-запросов. Для этого было бы вполне разумно использовать отдельный управляемый bean-компонент, который не имеет какого-либо состояния. Более того, InputStream может быть прочитано только один раз, а не несколько раз.

Другими словами: никогда не объявляйте StreamedContent, ни InputStream, ни даже UploadedFile как свойство бина; Создавайте его совершенно новым в получателе bean-компонента @ApplicationScoped без состояния, когда веб-браузер действительно запрашивает содержимое изображения .

1036 * Е.Г. *

<p:dataTable value="#{bean.students}" var="student">
    <p:column>
        <p:graphicImage value="#{studentImages.image}">
            <f:param name="studentId" value="#{student.id}" />
        </p:graphicImage>
    </p:column>
</p:dataTable>

Где StudentImages поддерживающий боб может выглядеть так:

@Named // Or @ManagedBean
@ApplicationScoped
public class StudentImages {

    @EJB
    private StudentService service;

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

        if (context.getCurrentPhaseId() == PhaseId.RENDER_RESPONSE) {
            // So, we're rendering the HTML. Return a stub StreamedContent so that it will generate right URL.
            return new DefaultStreamedContent();
        }
        else {
            // So, browser is requesting the image. Return a real StreamedContent with the image bytes.
            String studentId = context.getExternalContext().getRequestParameterMap().get("studentId");
            Student student = studentService.find(Long.valueOf(studentId));
            return new DefaultStreamedContent(new ByteArrayInputStream(student.getImage()));
        }
    }

}

Обратите внимание, что это особый случай, когда выполнение бизнес-логики в методе получения вполне законно, учитывая, как <p:graphicImage> работает под прикрытием. Вызывает бизнес-логику в методах получения, как правило, неодобрительно, см. Также Почему JSF вызывает методы получения несколько раз . Не используйте этот особый случай в качестве оправдания для других стандартных (не специальных) случаев. Также обратите внимание, что вы не можете использовать функцию EL 2.2 для передачи аргументов метода, например #{studentImages.image(student.id)}, потому что этот аргумент не попадет в URL изображения. Таким образом, вам действительно нужно передать их как <f:param>.


Если вам случится использовать OmniFaces 2.0 или новее , тогда рассмотрите возможность использования вместо него <o:graphicImage>, который можно использовать более интуитивно, с методом получателя области приложения, непосредственно делегирующим метод обслуживания и поддержка аргументов метода EL 2.2.

Таким образом, так:

<p:dataTable value="#{bean.students}" var="student">
    <p:column>
        <o:graphicImage value="#{studentImages.getImage(student.id)}" />
    </p:column>
</p:dataTable>

С

@Named // Or @ManagedBean
@ApplicationScoped
public class StudentImages {

    @EJB
    private StudentService service;

    public byte[] getImage(Long studentId) {
        return studentService.find(studentId).getImage();
    }

}

См. Также блог на эту тему.

5 голосов
/ 27 сентября 2012

Попробуйте включить тип пантомимы. В опубликованном вами примере это выглядит как "". Пустое изображение может быть связано с тем, что оно не распознает поток как файл изображения, поскольку вы сделали это поле пустой строкой. Поэтому добавьте mime-тип image / png или image / jpg и посмотрите, работает ли он:

String mimeType = "image/jpg";
StreamedContent file = new DefaultStreamedContent(bytes, mimeType, filename);  
4 голосов
/ 27 ноября 2011

Здесь есть несколько возможностей (и, пожалуйста, опубликуйте весь класс, если это не так).

1) Вы неправильно инициализируете изображение 2) Ваш поток пуст, поэтому вы получаетеНичего

Я предполагаю, что student.getImage () имеет сигнатуру byte [], поэтому сначала убедитесь, что эти данные действительно не повреждены и представляют изображение.Во-вторых, вы не указываете тип контента, который должен быть «image / jpg» или что вы используете.

Вот некоторый шаблонный код, чтобы проверить его, для этого я использую Primefaces 2.

/** 'test' package with 'test/test.png' on the path */
@RequestScoped
@ManagedBean(name="imageBean")
public class ImageBean
{
    private DefaultStreamedContent content;

    public StreamedContent getContent()
    {
        if(content == null)
        {
            /* use your database call here */
            BufferedInputStream in = new BufferedInputStream(ImageBean.class.getClassLoader().getResourceAsStream("test/test.png"));
            ByteArrayOutputStream out = new ByteArrayOutputStream();

            int val = -1;
            /* this is a simple test method to double check values from the stream */
            try
            {
                while((val = in.read()) != -1)
                    out.write(val);
            }
            catch(IOException e)
            {
                e.printStackTrace();
            }

            byte[] bytes = out.toByteArray();
            System.out.println("Bytes -> " + bytes.length);
            content = new DefaultStreamedContent(new ByteArrayInputStream(bytes), "image/png", "test.png");
        }

        return content;
    }
}

и некоторую разметку ...

<html 
    xmlns="http://www.w3.org/1999/xhtml" 
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:p="http://primefaces.prime.com.tr/ui"
>

    <h:head>

    </h:head>

    <h:body>
        <p:graphicImage value="#{imageBean.content}" />
    </h:body>
</html>

Если этокод работает, то вы настроены правильно.Несмотря на то, что это мусорный код для потоков (не используйте его в рабочей среде) , он должен дать вам точку для устранения неполадок.Я предполагаю, что у вас может что-то происходить в вашей JPA или другой структуре базы данных, где ваш байт [] пуст или неправильно отформатирован.В качестве альтернативы вы могли бы просто иметь проблему с типом контента.

Наконец, я бы клонировал данные из bean-компонента, чтобы student.getImage () только копировался в новый массив и затем использовался.Таким образом, если у вас происходит что-то неизвестное (что-то еще перемещает объект или изменяет байт [], вы не мешаете своим потокам.

Сделайте что-то вроде:

byte[] data = new byte[student.getImage().length]
for(int i = 0; i < data.length; i++)
  data[i] = student.getImage()[i];

такчто у вашего bean-компонента есть копия (или Arrays.copy () - все, что плавает на вашей лодке). Я не могу не подчеркнуть, что что-то простое, например, этот тип контента, обычно не в порядке. Удачи с этим.

3 голосов
/ 01 сентября 2016

Ответ от BalusC (как обычно) правильный.

Но имейте в виду одну вещь (как он уже сказал).Последний запрос выполняется из браузера для получения URL-адреса из созданного тега <img>.Это не делается в «контексте jsf».

Так что если вы попытаетесь, например, получить доступ к phaseId (запись в журнал или по любой другой причине)

context.getCurrentPhaseId().getName()

Это приведет к NullPointerException ичто-то вводящее в заблуждение сообщение об ошибке:

org.primefaces.application.resource.StreamedContentHandler () - Error in streaming dynamic resource. Error reading 'image' on type a.b.SomeBean

Мне потребовалось довольно много времени, чтобы выяснить, в чем проблема.

...