5

Image在我的 ManagedBean 中有对象。如何在我的 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

4

2 回答 2

7

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

必须代表一个有效的<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,那么您可以改用它<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" />

也可以看看:

于 2013-02-25T19:29:42.297 回答
2

除了调用 servlet 之外,还有一种方法:嵌入 Base64 编码的图像

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

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

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

好的,现在获取说明:

您的 Facelet 将简单地包括您的<h:graphicImage>

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

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

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()方法将 a byte[](包含图像内容)转换为 a String(bean 值)。您必须阅读 API 文档和/或 google 以了解更多详细信息,但它看起来非常简单明了,因为编码功能已经在提供的库中。

希望这是显而易见的,但图像只显示在第一个页面加载时(或者当图像通过例如 Ajax 更新时)。因此,如果您想更改图像,您可能会imgContentsBase64通过 Ajax 调用侦听器(等)更新内容render,至少还要更新myimageid组件。

于 2016-06-14T12:19:36.757 回答