I have got the Image
object in my ManagedBean. How can I get it in my JSF page?
This seems to be not working:
<h:graphicImage value="#{userProfile.image}" />
where image is a field variable in the class userProfile.
The Image is got from MySql as below.
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);
}
The error I got is:java.lang.ClassCastException - sun.awt.image.ToolkitImage cannot be cast to java.lang.String
.
There's a major misunderstanding here. JSF is basically a HTML code producer. In HTML, images are not inlined in the HTML output. Instead, they are supposed to be represented by <img>
elements with a (relative) URL in the src
attribute which the browser has to download individually during parsing the obtained HTML output. Look at the generated HTML output, the JSF <h:graphicImage>
component generates a HTML <img>
element which must have a src
attribute pointing to a valid URL.
The <h:graphicImage value>
must represent a valid URL. If you've however stored images in the DB instead of in the public webcontent, then you should basiclly be creating a standalone servlet which reads the individual image from the DB based on some unique request parameter or URL path and writes it to the response body.
So assuming that you render the image URL as follows from now on,
<h:graphicImage value="/userProfileImageServlet?id=#{userProfile.id}" />
then the following kickoff example (trivial checks like nullchecks etc omitted) of the servlet should do:
@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);
}
}
}
If you happen to use JSF utility library OmniFaces on a JSF 2.2 + CDI environment, then you can instead use its <o:graphicImage>
which could be used more intuitively.
<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);
}
}
}
It also transparently supports date URI scheme by just setting dataURI="true"
:
<o:graphicImage value="#{userProfileImageBean.getBytes(userProfile.id)}" dataURI="true" />
There is a way other than calling a servlet: embedding a Base64 encoded image.
Before I give pointers as how to do it, I first need to stress that THIS IS PROBABLY A BAD IDEA (especially if you do not know what the issues are and blindly use this technique). Some of the most important points:
Some more reading about the above-mentioned warnings (also follow links and google):
Your Facelet would simply include your <h:graphicImage>
:
<h:graphicImage id="myimageid" value="${myBean.imgContentsBase64}" />
... and your backing bean MyBean
would have a member:
private String imgContentsBase64; // including g/setter
The content of imgContentsBase64
should be of the format: "data:{MIME_TYPE};base64,{BASE64_ENCODED_CONTENTS}
".
Where:
image/png
, image/jpg
, etc.(refer back to the page referenced in the first line, it contains an example).
To do the latter, you get an encoder via java.util.Base64.getUrlEncoder()
(or .getEncoder()
or .getMimeEncoder()
but the first-mentioned is probably appropriate for this case). It seems you would then use the Base64.Encoder.encodeToString()
method to convert from a byte[]
(containing the image contents) to a String
(the bean value). You'll have to read the API docs and/or google for more details, but it looks quite straight-forward and easy, as the encoding functionality is already in the provided library.
And hopefully this is obvious, but the image is only displayed on the first page load (or when the image is updated via e.g. Ajax). So if you want to change the image, you would probably update the imgContentsBase64
contents via an Ajax call listener (etc.), and also render
at least the myimageid
component.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With