<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();
}
}
См. Также блог на эту тему.