【问题标题】:Writing image to servlet response with best performance以最佳性能将图像写入 servlet 响应
【发布时间】:2011-02-28 02:46:14
【问题描述】:

我正在以最佳性能将图像写入 servlet 响应。有什么建议、做法、经验吗?

【问题讨论】:

  • 我将图像保存在当前项目的数据库中。特定的 spring 控制器从 DB 中获取图像的字节数组并将它们写入 servlet 响应。在 servlet 容器前面还会有一个 Web 服务器。据我所知,这可能会导致一些性能问题。现在我没有麻烦,但想知道是否有一些好的解决方案。例如。一些缓存实践,特定的标头等。赞赏一些关于 DB 与 FileSystem 作为图像存储的经验。在我看来,将图像存储在数据库中更舒服。

标签: java performance image servlets


【解决方案1】:

如果图像是静态的,请记住最快的响应是在它到达您之前处理的。

你可以在你的 Tomcat 服务器前面架起 Apache 的 httpd。您可以使用缓存边缘服务器的其他变体。这些方面有很多技巧。

当然,这假设您的应用程序是这样编写的,其中 URL 以一种易于缓存的方式有效地映射到一个图像。如果您的应用程序缺少这一点,那么好处就足以考虑重组。

【讨论】:

    【解决方案2】:

    为了获得最佳性能和效率,请勿将全部内容放入byte[]。每个byte 都会占用Java 内存的一个字节。想象一下 100 个并发用户请求每 100KB 的 10 张图像,这已经消耗了 100MB 的 Java 内存。

    使用ResultSet#getBinaryStream() 从数据库中以InputStream 形式获取图像,将其包装在BufferedInputStream 中,并通过一个小的byte[] 缓冲区将其写入包装在BufferedOutputStream 中的响应的OutputStream .

    假设您通过数据库键选择图像作为标识符,请在 HTML 中使用:

    <img src="images/123">
    

    创建一个Servlet 类,该类映射到web.xml 中的url-pattern/images/* 上,并按如下方式实现其doGet() 方法:

    Long imageId = Long.valueOf(request.getPathInfo().substring(1)); // 123
    Image image = imageDAO.find(imageId); // Get Image from DB.
    // Image class is just a Javabean with the following properties:
    // private String filename;
    // private Long length;
    // private InputStream content;
    
    response.setHeader("Content-Type", getServletContext().getMimeType(image.getFilename()));
    response.setHeader("Content-Length", String.valueOf(image.getLength()));
    response.setHeader("Content-Disposition", "inline; filename=\"" + image.getFilename() + "\"");
    
    BufferedInputStream input = null;
    BufferedOutputStream output = null;
    
    try {
        input = new BufferedInputStream(image.getContent());
        output = new BufferedOutputStream(response.getOutputStream());
        byte[] buffer = new byte[8192];
        for (int length = 0; (length = input.read(buffer)) > 0) {
            output.write(buffer, 0, length);
        }
    } finally {
        if (output != null) try { output.close(); } catch (IOException logOrIgnore) {}
        if (input != null) try { input.close(); } catch (IOException logOrIgnore) {}
    }
    

    ImageDAO#find() 中,您可以使用ResultSet#getBinaryStream() 从数据库中以InputStream 的形式获取图像。

    【讨论】:

    • 对此的一个小改动可能是允许 ImageDAO 写回响应,或者可能使用类似样式的回调(想想 spring JdbcTemplate 或 HibernateTemplate)。否则 JDBCConnection 和 ResultSet 在从 ImageDAO.find() 返回后必须保持打开状态,以便 Image#content InputStream 工作。
    • 嗨,当你说“不要使用字节[]”时,你使用的是byte[] buffer = new byte[8192];,那么有什么意义呢?
    • @hguser:我澄清了答案。
    • @BalusC:谢谢!然后我想知道如果我的应用程序应该在内存中缓存BufferedImage,更好的做法是什么?我发现如果我直接缓存BufferedImage可能会发生内存泄漏,然后我将图像转换为byte[]并缓存它们,所以看起来这不是一个好的解决方案?
    • @BalusC 如果在数据库中找不到图像(blob 为 NULL),如何返回默认图像?
    【解决方案3】:

    如果它存在于 Blob 类型的数据库中,您可以将数组类型的字节用于来自 servlet 的图像。

    byte[] image;
    

    或者还有另外一种方法,但是有点复杂。当你调用你的servlet时,在此之前你需要确定调用是图像还是正常调用。如果是正常调用,那么您可以继续调用 servlet,但如果是图像调用,则不要调用 servlet,但您可以将图像引用存储在计算机中的某个物理位置并检索它们。

    但是如果您在 DB 中有图像,则此方法将不起作用,而是您可以在 DB 中有相对路径,然后您可以从该路径获取图像。

    【讨论】:

      猜你喜欢
      • 2012-10-30
      • 2013-04-10
      • 2021-07-26
      • 1970-01-01
      • 1970-01-01
      • 2011-03-01
      • 2010-10-22
      • 2011-02-17
      • 1970-01-01
      相关资源
      最近更新 更多