我在 Spring MVC 应用程序中有一些代码可以查询数据库并从结果集中构建 CSV 文件。总体思路是这样的:
@RequestMapping
(value = "/path/to/the/data")
public ModelAndView getDataAsCsv(...) {
List<RowObject> bigObject; // can be > 1GB in memory
bigObject = dataService.getData(...);
ModelAndView mav = new ModelAndView("dataCsvView");
mav.addObject("bigObject", bigObject);
return mav;
}
然后我们有一个DataCsvView
扩展AbstractView
和覆盖的类renderMergedOutputModel
。它所做的第一件事就是:
List<RowObject> bigObject = (ArrayList<RowObject>)model.get("bigObject");
然后它继续迭代这个东西,将每一行转换为一个 CSV 字符串,并将其写入BufferedWriter
来自 SpringHttpServletResponse#getWriter
方法的那个。
问题
如果数据集足够大(超过约 500k 行),那么bigObject
内存中的内容将 > 1GB。一旦renderMergedOutputModel
被调用并尝试将对象从模型中取出,应用程序就会抛出 OutOfMemoryError。或者,错误可能在renderMergedOutputModel
被调用之前发生。无论哪种方式,将对象放入mav
地图的行为都不是失败点(我已经检查过)。
一种解决方案是增加 Tomcat 进程可用的内存,但这显然不是很可扩展……(这些bigObject
s 将来可能会变得更大)
在 Spring MVC 中执行此操作的“正确”方法是什么?是否有一个不错的架构可以让我们流式传输响应而无需先将其全部存储在内存中?