如何从数据库中获取图像内容并将其赋值给h:graphicImage的值?

5

我在我的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 无法转换为 java.lang.String
2个回答

7
这里存在一个重大误解。JSF基本上是一个HTML代码生成器。在HTML中,图像不会内联在HTML输出中。相反,它们应该由<img>元素表示,其中包含(相对)URL在src属性中,浏览器必须在解析获得的HTML输出时单独下载它们。查看生成的HTML输出,JSF <h:graphicImage>组件生成一个HTML <img>元素,必须具有指向有效URL的src属性。 <h:graphicImage value>必须表示有效的URL。但是,如果您将图像存储在数据库中而不是公共Web内容中,则应基本上创建一个独立的servlet,该servlet根据某些唯一请求参数或URL路径从DB中读取各个图像并将其写入响应正文。
因此,假设您从现在开始按以下方式呈现图像URL,
<h:graphicImage value="/userProfileImageServlet?id=#{userProfile.id}" />

接下来这个启动示例(忽略了像空值检查等)的servlet应该做:

@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"来透明地支持日期URI方案:
<o:graphicImage value="#{userProfileImageBean.getBytes(userProfile.id)}" dataURI="true" />

另请参阅:


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

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

  • Base64编码效率低下,使用此技术会使用大约33%更多的数据(但您将使用较少的HTTP请求,每个请求都有自己的开销,因此如果您的图像非常小,则~33%的低效性可能仍然小于额外的HTTP请求。)
  • 某些旧版Web浏览器不支持“data:…”协议,因此使用此技术不会显示图像。
  • 根据各种配置,嵌入式图像的HTML页面可能不会被缓存,而从servlet生成的图像可能会被缓存,因此此技术将再次导致更大的低效性。再次具备知识可以帮助这里...(略微偏离话题,将其用于CSS文件可能是有意义的-例如小的背景图像-如果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/png, image/jpg等。
  • {BASE64_ENCODED_CONTENTS} 是二进制图像文件编码后的字节。
(参考第一行引用的页面,它包含了一个示例)。
要完成后面的任务,您需要通过java.util.Base64.getUrlEncoder()来获取一个编码器(或者使用.getEncoder().getMimeEncoder(),但对于这种情况,第一种可能更适合)。然后似乎您可以使用Base64.Encoder.encodeToString()方法将byte[](包含图像内容)转换为String(bean值)。您需要阅读API文档和/或搜索更多细节,但看起来非常简单易懂,因为编码功能已经在提供的库中。 希望这很明显,但图像只在第一次加载页面时显示(或通过Ajax更新图像时)。所以如果要更改图像,您可能会通过Ajax调用监听器(等等)更新imgContentsBase64的内容,并render至少myimageid组件。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接