我在Cassandra表中分割了一个大型视频文件。我正在尝试使用Source
流式传输回流到API客户端。
我的服务代码如下所示,
def getShards(id: String, shards: Int) = {
def getShardsInternal(shardNo: Int, shards: Future[Array[Byte]]): Future[Array[Byte]] = {
if (shardNo == 0) shards
else getShardsInternal(shardNo - 1, shards.flatMap(x => Database.ShardModel.find(id, shardNo)))
}
getShardsInternal(shards, Future.successful(Array()))
}
在我的AKKA HTTP路由中,我尝试从返回的未来构建Source
,如下所示,
def getAsset = get {
pathPrefix("asset") {
parameters('id) { id =>
complete {
val f = mediaService.getMetadata(id).flatMap { x =>
mediaService.getShards(id, x.shards)
}
Source.fromFuture(f)
}
}
}
}
我不确定Source.fromFuture
如何致力于响应。通过的未来基本上是一系列预期按顺序执行的平面映射期货。但是,我不相信这会作为一个分块的字节流返回给客户端。
对此的任何指示都将受到高度赞赏。
编辑1 我一直试图通过以下方式进一步缩小范围,
get {
pathPrefix("asset") {
parameters('id) { id =>
complete {
Source.fromFuture {
Future.successful("Hello".getBytes()).flatMap(x => Future.successful("World".getBytes()))
}
}
}
}
}
我原本希望这会回来
[72,101,108,108,111,32,87,111,114,108,100]
但是,我只能得到下一个结果,
[[87,111,114,108,100]]
亲切的问候 Meeraj
答案 0 :(得分:2)
将您的Source[Array[Byte], NotUsed]
转换为Source[ByteString, NotUsed]
,并将HttpEntity
与ContentTypes
一起使用:
import akka.util.ByteString
def getAsset = get {
pathPrefix("asset") {
parameters('id) { id =>
val f = mediaService.getMetadata(id).flatMap { x =>
mediaService.getShards(id, x.shards)
}
val source = Source.fromFuture(f).map(ByteString.apply)
complete(HttpEntity(ContentTypes.`application/octet-stream`, source))
}
}
}
我在这里以application/octet-stream
为例。由于您要对视频进行流式传输,因此您可能需要将ContentType.Binary
与适当的media type一起使用。例如:
complete(HttpEntity(ContentType.Binary(MediaTypes.`video/mpeg`), source))
解决您的评论和更新,您似乎希望在getShards
中连接期货的结果:正如您发现的那样,flatMap
没有这样做。请改用Future.reduceLeft
:
def getShards(id: String, shards: Int): Future[Array[Byte]] = {
val futures = (1 to shards).map(Database.ShardModel.find(id, _))
Future.reduceLeft(futures)(_ ++ _)
}
或者,您可以重新定义getShards
以返回Future[List[Array[Byte]]]
,然后使用Source
创建flatMapConcat
,而不是将结果连接到单个数组中:
def getShards(id: String, shards: Int): Future[List[Array[Byte]]] = {
val futures = (1 to shards).map(Database.ShardModel.find(id, _)).toList
Future.sequence(futures)
}
def getAsset = get {
pathPrefix("asset") {
parameters('id) { id =>
val f = mediaService.getMetadata(id).flatMap { x =>
mediaService.getShards(id, x.shards)
}
val source =
Source.fromFuture(f)
.flatMapConcat(Source.apply)
.map(ByteString.apply)
complete(HttpEntity(/* a content type */, source))
}
}
}