繁体   English   中英

如何使用Play 2.5在分块响应中将Anorm大型查询结果流式传输到客户端

[英]How to stream Anorm large query results to client in chunked response with Play 2.5

我有一个很大的结果集(超过60k条记录列),我从数据库中提取并用Anorm进行了解析(尽管我可以使用play的默认数据访问模块,如果需要的话,该模块会返回ResultSet)。 我需要将这些结果直接转换并流式传输到客户端(而不将其保存在内存中的大列表中),然后将它们直接下载到客户端计算机上的文件中。

我一直在参考ScalaStream 2.5.x Play文档中“块状响应”部分中展示的内容。 我在实现其中显示的内容的“ getDataStream”部分时遇到了麻烦。

我还一直在参考ScalaAnorm 2.5.x Play文档中“流式结果”和“ Iteratee”部分中演示的内容。 我已经尝试将结果作为枚举器传递给管道,就像这里返回的内容一样:

 val resultsEnumerator = Iteratees.from(SQL"SELECT * FROM Test", SqlParser.str("colName"))

val dataContent = Source.fromPublisher(Streams.enumeratorToPublisher(resultsEnumerator))
Ok.chunked(dataContent).withHeaders(("ContentType","application/x-download"),("Content-disposition","attachment; filename=myDataFile.csv"))

但是结果文件/内容为空。

而且我找不到任何示例代码或有关如何转换数据服务中返回以下内容的函数的示例代码:

@annotation.tailrec
def go(c: Option[Cursor], l: List[String]): List[String] = c match {
  case Some(cursor) => {
    if (l.size == 10000000) l // custom limit, partial processing
    else {
      go(cursor.next, l :+ cursor.row[String]("VBU_NUM"))
    }
  }
  case _ => l
}

val sqlString = s"select colName FROM ${tableName} WHERE ${whereClauseStr}"

val results : Either[List[Throwable], List[String]] = SQL(sqlString).withResult(go(_, List.empty[String]))
results

可以传递给Ok.chunked()。

因此,基本上我的问题是,我应该如何将从数据库中提取的每个记录馈送到可以进行转换的流中,并作为可以下载到文件的分块响应发送给客户端?

我不希望为此使用Slick。 但是我可以采用不使用Anorm的解决方案,而只使用play dbApi对象,该对象返回原始java.sql.ResultSet对象并使用该对象。

在参考了Anorm Akka支持文档并进行了多次试验和错误之后,我能够实现所需的解决方案。 我必须添加这些依赖项

"com.typesafe.play" % "anorm_2.11" % "2.5.2",
"com.typesafe.play" % "anorm-akka_2.11" % "2.5.2",
"com.typesafe.akka" %% "akka-stream" % "2.4.4"

到Play 2.5的build.sbt文件。

我实现了这样的事情

//...play imports
import anorm.SqlParser._
import anorm._

import akka.actor.ActorSystem
import akka.stream.ActorMaterializer
import akka.stream.scaladsl.{Sink, Source}

...

private implicit val akkaActorSystem = ActorSystem("MyAkkaActorSytem")
private implicit val materializer = ActorMaterializer()

def streamedAnormResultResponse() = Action {
  implicit val connection = db.getConnection()

  val parser: RowParser[...] = ...
  val sqlQuery: SqlQuery = SQL("SELECT * FROM table")

  val source: Source[Map[String, Any] = AkkaStream.source(sqlQuery, parser, ColumnAliaser.empty).alsoTo(Sink.onComplete({
    case Success(v) =>
      connection.close()
    case Failure(e) =>
      println("Info from the exception: " + e.getMessage)
      connection.close()
  }))

  Ok.chunked(source)
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM