Как заполнить значение h:graphicImage содержимым изображения из базы данных?
У меня есть Image
объект в моем ManagedBean. Как я могу получить это на моей странице JSF?
Это, кажется, не работает:<h:graphicImage value="#{userProfile.image}" />
где изображение - это переменная поля в классе userProfile.
Изображение получено с MySql, как показано ниже.
int len = rs.getInt("ImageLen");
if(len != 0){
byte[] b = new byte[len];
InputStream in = rs.getBinaryStream("Image");
in.read(b);
in.close();
this.image = Toolkit.getDefaultToolkit().createImage(b);
}
Я получил ошибку:java.lang.ClassCastException - sun.awt.image.ToolkitImage cannot be cast to java.lang.String
,
2 ответа
Здесь есть серьезное недоразумение. JSF в основном производитель HTML-кода. В HTML изображения не встраиваются в вывод HTML. Вместо этого они должны быть представлены <img>
элементы с (относительным) URL в src
атрибут, который браузер должен загружать индивидуально во время анализа полученного HTML-вывода. Посмотрите на сгенерированный вывод HTML, JSF <h:graphicImage>
компонент генерирует HTML <img>
элемент, который должен иметь src
атрибут, указывающий на действительный URL.
<h:graphicImage value>
должен представлять действительный URL. Однако если вы сохранили изображения в БД, а не в общедоступном веб-контенте, то вам следует создать автономный сервлет, который считывает отдельное изображение из БД на основе какого-либо уникального параметра запроса или пути URL-адреса и записывает его в тело ответа.,
Итак, предположим, что теперь вы отображаете URL изображения следующим образом:
<h:graphicImage value="/userProfileImageServlet?id=#{userProfile.id}" />
затем следует выполнить следующий пример запуска (тривиальные проверки, такие как nullchecks и т. д.) сервлета:
@WebServlet("/userProfileImageServlet")
public class UserProfileImageServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Long userProfileId = Long.valueOf(request.getParameter("id"));
try (
Connection connection = dataSource.getConnection();
PreparedStatement statement = connection.prepareStatement("SELECT image, imageFileName, LENGTH(image) AS imageContentLength FROM userProfile WHERE id=?");
) {
statement.setLong(1, userProfileId);
try (ResultSet resultSet = statement.executeQuery()) {
if (resultSet.next()) {
response.setContentType(getServletContext().getMimeType(resultSet.getString("imageFileName")));
response.setContentLength(resultSet.getInt("imageContentLength"));
response.setHeader("Content-Disposition", "inline;filename=\"" + resultSet.getString("imageFileName") + "\"");
try (
ReadableByteChannel input = Channels.newChannel(resultSet.getBinaryStream("image"));
WritableByteChannel output = Channels.newChannel(externalContext.getResponseOutputStream());
) {
for (ByteBuffer buffer = ByteBuffer.allocateDirect(10240); input.read(buffer) != -1; buffer.clear()) {
output.write((ByteBuffer) buffer.flip());
}
}
}
} else {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
}
} catch (SQLException e) {
throw new ServletException("Something failed at SQL/DB level.", e);
}
}
}
Если вам случится использовать служебную библиотеку JSF OmniFaces в среде JSF 2.2 + CDI, вы можете вместо этого использовать ее <o:graphicImage>
который может быть использован более интуитивно.
<o:graphicImage value="#{userProfileImageBean.getBytes(userProfile.id)}" />
@Named
@ApplicationScoped
public class UserProfileImageBean {
public byte[] getBytes(Long userProfileId) {
try (
Connection connection = dataSource.getConnection();
PreparedStatement statement = connection.prepareStatement("SELECT image FROM userProfile WHERE id=?");
) {
statement.setLong(1, userProfileId);
try (ResultSet resultSet = statement.executeQuery()) {
if (resultSet.next()) {
return resultSet.getBytes("image");
}
} else {
return null;
}
} catch (SQLException e) {
throw new FacesException("Something failed at SQL/DB level.", e);
}
}
}
Он также прозрачно поддерживает схему URI даты, просто установив dataURI="true"
:
<o:graphicImage value="#{userProfileImageBean.getBytes(userProfile.id)}" dataURI="true" />
Смотрите также:
Есть и другой способ, кроме вызова сервлета: встраивание изображения в кодировке Base64.
Прежде чем дать указания о том, как это сделать, я сначала должен подчеркнуть, что ЭТО ВЕРОЯТНО ПЛОХАЯ ИДЕЯ (особенно если вы не знаете, в чем заключаются проблемы, и слепо используете эту технику). Некоторые из самых важных моментов:
- Кодировка Base64 неэффективна, и вы будете использовать примерно на 33% больше данных с этим методом. (Однако вы бы использовали меньше HTTP-запросов, каждый из которых имеет свои накладные расходы, поэтому, если ваше изображение очень маленькое, неэффективность ~33% все равно может быть меньше, чем у дополнительного HTTP-запроса.)
- Некоторые старые веб-браузеры не поддерживают протокол "data:...", поэтому изображения не будут отображаться с помощью этого метода.
- В зависимости от различных конфигураций HTML-страница со встроенным изображением не будет кэшироваться, в то время как изображение, сгенерированное из сервлета, МОЖЕТ быть кэшировано, поэтому этот метод снова вызовет большую неэффективность. В этом вам может помочь знание новых возможностей... (При некотором касании использование этого в файлах CSS может иметь смысл - например, небольшие фоновые изображения - ЕСЛИ файл CSS кэшируется - как это часто бывает).
Еще немного прочтения о вышеупомянутых предупреждениях (также перейдите по ссылкам и в Google):
- Stackru: преимущества и недостатки кодирования изображений base64
- Блог Дэвида Кэлхуна: когда кодировать изображения Base64 (а когда нет)
- Stackru: Является ли встраивание данных фонового изображения в CSS как Base64 хорошей или плохой практикой?
Хорошо, теперь для инструкций:
Ваш Facelet будет просто включать ваш <h:graphicImage>
:
<h:graphicImage id="myimageid" value="${myBean.imgContentsBase64}" />
... и ваш боб MyBean
будет иметь члена:
private String imgContentsBase64; // including g/setter
Содержание imgContentsBase64
должен быть в формате: "data:{MIME_TYPE};base64,{BASE64_ENCODED_CONTENTS}
".
Куда:
- {MIME_TYPE} например
image/png
,image/jpg
, так далее. - {BASE64_ENCODED_CONTENTS} - это байты закодированного двоичного файла изображения.
(вернитесь к странице, указанной в первой строке, она содержит пример).
Чтобы сделать последнее, вы получите кодировщик через java.util.Base64.getUrlEncoder()
(или же .getEncoder()
или же .getMimeEncoder()
но первое упомянутое, вероятно, подходит для этого случая). Кажется, вы бы тогда использовали Base64.Encoder.encodeToString()
метод для преобразования из byte[]
(содержащий содержимое изображения) в String
(бобовое значение). Вам нужно будет прочитать документы API и / или Google для получения более подробной информации, но это выглядит довольно просто и легко, так как функциональность кодирования уже есть в предоставленной библиотеке.
И, надеюсь, это очевидно, но изображение отображается только при первой загрузке страницы (или когда изображение обновляется, например, с помощью Ajax). Так что если вы хотите изменить изображение, вы, вероятно, обновите imgContentsBase64
содержимое через прослушиватель вызовов Ajax (и т. д.), а также render
По крайней мере myimageid
составная часть.