如何使用数据库中的图像内容填充h:graphicImage值?

时间:2013-02-25 19:04:50

标签: database jsf bytearray blob graphicimage

我的ManagedBean中有Image个对象。如何在我的JSF页面中获取它?

这似乎不起作用: <h:graphicImage value="#{userProfile.image}" />其中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 个答案:

答案 0 :(得分:6)

这里有一个重大的误解。 JSF基本上是一个HTML代码生成器。在HTML中,HTML输出中没有内嵌图像。相反,它们应该由<img>元素表示,src属性中的(相对)URL,浏览器在解析获得的HTML输出时必须单独下载。查看生成的HTML输出,JSF <h:graphicImage>组件生成一个HTML <img>元素,该元素必须具有指向有效URL的src属性。

<h:graphicImage value>必须代表有效的网址。但是,如果您已将图像存储在数据库中而不是公共Web内容中,那么您应该基本上创建一个独立的servlet,它根据某个唯一的请求参数或URL路径从数据库中读取单个图像并写入回应机构。

因此,假设您从现在开始如下呈现图像URL,

<h:graphicImage value="/userProfileImageServlet?id=#{userProfile.id}" />

然后servlet应该执行以下启动示例(省略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 2.2 + CDI环境中使用JSF实用程序库OmniFaces,那么您可以使用可以更直观地使用的<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);
        }
    }

}

通过设置dataURI="true"

,它还可以透明supports日期URI方案
<o:graphicImage value="#{userProfileImageBean.getBytes(userProfile.id)}" dataURI="true" />

另见:

答案 1 :(得分:1)

除了调用servlet之外,还有一种方法: embedding a Base64 encoded image

在我指明如何做之前,我首先需要强调这可能是一个糟糕的想法(特别是如果你不知道问题是什么并且盲目地使用这种技术)。一些最重要的观点:

  • Base64编码效率低下,您可以使用此技术粗略 33%以上数据。 (但是,您会使用较少的HTTP请求,每个请求都有自己的开销,因此 IF 您的图像非常小,〜33%的低效率可能仍然小于额外HTTP请求的效率。)< / LI>
  • 某些较旧的网络浏览器不支持“data:...”协议,因此图片无法使用此技术显示。
  • 根据各种配置,带有嵌入图像的HTML页面不会被缓存,而从servlet生成的图像可能会被缓存,因此这种技术将再次导致效率降低。再次具备知识可以帮助...(略微切线,在CSS文件中使用它可能有意义 - 例如小背景图像 - IF CSS文件被缓存 - 通常是这种情况。)

关于上述警告的更多内容(也请关注链接和谷歌):

好的,现在是指示:

您的Facelet只会包含您的<h:graphicImage>

<h:graphicImage id="myimageid" value="${myBean.imgContentsBase64}" />

...并且您的支持bean MyBean将有一个成员:

private String imgContentsBase64; // including g/setter

imgContentsBase64的内容格式应为:“data:{MIME_TYPE};base64,{BASE64_ENCODED_CONTENTS}”。

其中:

  • {MIME_TYPE},例如image/pngimage/jpg
  • {BASE64_ENCODED_CONTENTS}是编码的二进制图像文件的字节。

(请参阅第一行中引用的页面,它包含一个示例)。

要执行后者,您可以通过java.util.Base64.getUrlEncoder()(或.getEncoder().getMimeEncoder()获得编码器,但首先提到的可能适用于此情况。然后,您似乎可以使用Base64.Encoder.encodeToString()方法将byte[](包含图像内容)转换为String(bean值)。您需要阅读API文档和/或Google以获取更多详细信息,但它看起来非常简单直接,因为编码功能已经在提供的库中。

希望这很明显,但图片仅显示在第一页加载上(或者通过例如Ajax更新图像时)。因此,如果您想要更改图像,您可能会通过Ajax调用侦听器(等)以及imgContentsBase64至少render组件更新myimageid内容。