我有一个大的 map 想要返回到前端。最初我将 map 转换为 jackson json 节点并使用 play 提供的 return ok() 方法将 map 返回给用户。
原始代码:
public Result returnResponse() {
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> returnMap = populateMapWithData();
JsonNode response = mapper.valueToTree(returnMap);
return ok(response);
}
由于 map 可能非常大,我遇到了内存问题。
在查看 play framework 文档时,有两种方法可以将大数据返回到前端。如果大小已知,我可以将数据流回给用户。如果大小未知,我可以分块提供数据。
播放框架文档:https://www.playframework.com/documentation/2.8.x/JavaStream
对于流式传输:
public Result index() {
java.io.File file = new java.io.File("/tmp/fileToServe.pdf");
java.nio.file.Path path = file.toPath();
Source<ByteString, ?> source = FileIO.fromPath(path);
Optional<Long> contentLength = null;
try {
contentLength = Optional.of(Files.size(path));
} catch (IOException ioe) {
throw new RuntimeException(ioe);
}
return new Result(
new ResponseHeader(200, Collections.emptyMap()),
new HttpEntity.Streamed(source, contentLength, Optional.of("text/plain")));
}
对于分块:
public Result index() {
// Prepare a chunked text stream
Source<ByteString, ?> source =
Source.<ByteString>actorRef(256, OverflowStrategy.dropNew())
.mapMaterializedValue(
sourceActor -> {
sourceActor.tell(ByteString.fromString("kiki"), null);
sourceActor.tell(ByteString.fromString("foo"), null);
sourceActor.tell(ByteString.fromString("bar"), null);
sourceActor.tell(new Status.Success(NotUsed.getInstance()), null);
return NotUsed.getInstance();
});
// Serves this stream with 200 OK
return ok().chunked(source);
}
我的问题是:
- 如何为 Jackson json 节点获得相同的结果?
- 是否有另一种方法可以使用 play 框架解决大型数据集问题?
- 我们还有其他用于 JSON 流的播放框架文档吗?
回答1
我认为你必须实现流代码设计 f.e.:
- 函数
populateMapWithData()
必须返回数据流,一个返回元素 - 一个 json 节点(或节点数组,我认为 werry 小节点会使您的响应非常慢) - 函数
populateMapWithData()
必须逐段从数据库中获取数据(当流将尝试获取下一部分数据时),以节省内存 - 对于结果,您必须使用
Chunked responses
因为您不知道内容的实际大小,https://www.playframework.com/documentation/2.8.x/ScalaStream#Chunked-responses 中的更多详细信息与您提出的问题相同的链接 - 在客户端,您可以读取每个块或块数组,因为这批准了 json 规范
我让我们尝试制作不同的设计样本:
Results.ok().chunked( //chunked result
Source.fromPublisher(s -> // publisher read all data by chunks
mapper.valueToTree(populateEntryWithData(s))));
Object populateEntryWithData(Subscriber<? super T> s) {
//todo implement page reading for data
//todo or you can try use reactive driver for database
}
注意
使用 json 的游戏框架通常会更好地使用 play.libs.Json
或注入 ObjectMapper
或注入 ObjectMapperBuilder
。
如果您需要添加更多配置您可以调用 mapper.copy()
的对象映射器,否则您可能会破坏服务中的另一个内部映射函数