在 java servlet 中流式传输大文件

2022-08-31 23:59:31

我正在构建一个需要扩展的Java服务器。其中一个 servlet 将提供存储在 Amazon S3 中的映像。

最近在负载下,我的VM内存不足,这是在我添加代码来提供映像之后,所以我非常确定流式传输更大的servlet响应会导致我的麻烦。

我的问题是:在如何编写java servlet代码以在从数据库或其他云存储读取时将大型(>200k)响应流式传输回浏览器方面,是否有任何最佳实践?

我考虑过将文件写入本地临时驱动器,然后生成另一个线程来处理流,以便可以重用tomcat servlet线程。这似乎会很重。

任何想法将不胜感激。谢谢。


答案 1

如果可能,不应将要提供的文件的全部内容存储在内存中。相反,请为数据获取 InputStream,并将数据分段复制到 Servlet OutputStream。例如:

ServletOutputStream out = response.getOutputStream();
InputStream in = [ code to get source input stream ];
String mimeType = [ code to get mimetype of data to be served ];
byte[] bytes = new byte[FILEBUFFERSIZE];
int bytesRead;

response.setContentType(mimeType);

while ((bytesRead = in.read(bytes)) != -1) {
    out.write(bytes, 0, bytesRead);
}

// do the following in a finally block:
in.close();
out.close();

我同意toby的观点,你应该“将它们指向S3 url”。

至于 OOM 异常,您确定它与提供图像数据有关吗?假设您的 JVM 有 256MB 的“额外”内存用于提供图像数据。在谷歌的帮助下,“256MB / 200KB”= 1310。对于2GB的“额外”内存(现在是非常合理的数量),可以支持超过10,000个并发客户端。即便如此,1300 个并发客户端仍然是一个相当大的数字。这是您遇到的负载类型吗?如果没有,您可能需要在其他位置查找 OOM 异常的原因。

编辑 - 关于:

在此用例中,图像可能包含敏感数据...

几周前,当我通读 S3 文档时,我注意到您可以生成可附加到 S3 URL 的过期密钥。因此,您不必向公众打开S3上的文件。我对这项技术的理解是:

  1. 初始 HTML 页面包含指向您的 Web 应用的下载链接
  2. 用户点击下载链接
  3. 您的 Web 应用会生成一个 S3 URL,其中包含一个密钥,该密钥将在 5 分钟后过期。
  4. 使用步骤 3 中的 URL 向客户端发送 HTTP 重定向。
  5. 用户从 S3 下载文件。即使下载时间超过5分钟,这也有效 - 下载开始后,它可以继续完成。

答案 2

你为什么不直接指向S3网址呢?从S3中获取工件,然后通过您自己的服务器将其流式传输到我,这违背了使用S3的目的,即卸载向Amazon提供图像的带宽和处理。


推荐