【问题标题】:How to populate h:graphicImage value with image content from database?如何使用数据库中的图像内容填充 h:graphicImage 值?
【发布时间】:2013-02-11 00:56:19
【问题描述】:

我的 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

【问题讨论】:

    标签: database jsf bytearray blob graphicimage


    【解决方案1】:

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

    <h:graphicImage value> 必须代表一个有效的 URL。但是,如果您将图像存储在数据库中而不是公共网络内容中,那么您基本上应该创建一个独立的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,那么您可以改用它的 &lt;o:graphicImage&gt;,这样可以更直观地使用。

    <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" />
    

    另见:

    【讨论】:

      【解决方案2】:

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

      在我给出如何做的指导之前,我首先需要强调这可能是一个坏主意(尤其是如果你不知道问题是什么并且盲目地使用这种技术)。一些最重要的点:

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

      更多关于上述警告的阅读(也可以关注链接和谷歌):

      好的,现在看说明:

      您的 Facelet 只需包含您的 &lt;h:graphicImage&gt;

      <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 的内容,并且至少更新rendermyimageid 组件。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-04-12
        • 2011-05-12
        • 1970-01-01
        • 2017-09-06
        • 2012-01-19
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多