所谓文件交换指的是Http协议中服务端和客户端之间文件的上传和下载。Akka-http作为一种系统集成工具应该具备高效率的数据交换方式包括文件交换和数据库表行的上传下载。Akka-http的数据交换模式支持流式操作:代表交换数据可以是一种无限长度流的元素。这种模式首先解决了纯Http大数据通过Multipart传输所必须进行的数据分段操作和复杂的消息属性设定等需要的技术门槛,再者用户还可以很方便的使用Akka-stream对数据进行深度处理,免去了数据转换的麻烦。更重要的是:Akka-http还支持reactive-stream,可以避免由传输速率所产生的种种问题。在本篇我们讨论利用Akka-http进行文件的双向传递。

任何文件的内容储存格式无论在硬盘、内存或者数据线上都是一堆bytes。文件交换流程包括读取文件里的bytes,传送这些bytes,最终把这些bytes写入文件。我们看到这里每个环节操作目标都是bytes,所以可能在程序里是不需要任何数据转换过程的。Akka提供了一组文件读写函数,如下:

  def fromPath(f: Path, chunkSize: Int = ): Source[ByteString, Future[IOResult]] =
fromPath(f, chunkSize, startPosition = ) def fromPath(f: Path, chunkSize: Int, startPosition: Long): Source[ByteString, Future[IOResult]] =
Source.fromGraph(new FileSource(f, chunkSize, startPosition, DefaultAttributes.fileSource, sourceShape("FileSource"))) def toPath(f: Path, options: Set[OpenOption] = Set(WRITE, TRUNCATE_EXISTING, CREATE)): Sink[ByteString, Future[IOResult]] =
toPath(f, options, startPosition = ) def toPath(f: Path, options: Set[OpenOption], startPosition: Long): Sink[ByteString, Future[IOResult]] =
Sink.fromGraph(new FileSink(f, startPosition, options, DefaultAttributes.fileSink, sinkShape("FileSink")))

我们看到:fromPath类型是Source[ByteSgtring,_],toPath类型是Sink[ByteString,_],直接就是流型式,应该可以直接放入Http消息的Entity中,如下:

  def fileStream(filePath: String, chunkSize: Int): Source[ByteString,Any] = {
def loadFile = {
// implicit val ec = httpSys.dispatchers.lookup("akka.http.blocking-ops-dispatcher")
val file = Paths.get(filePath)
FileIO.fromPath(file, chunkSize)
.withAttributes(ActorAttributes.dispatcher("akka.http.blocking-ops-dispatcher"))
}
limitableByteSource(loadFile)
}

fileStream是Source[ByteString,_]可以直接放进Entity:

  val uploadText = HttpRequest(HttpMethods.POST,uri = s"http://localhost:8011/file/text")
val textData = HttpEntity(
ContentTypes.`application/octet-stream`,
fileStream("/Users/tiger-macpro/downloads/A4.TIF",)
)

我们把fileStream放入了HttpRequest中。对于HttpResponse可以用下面的方式:

 val route = pathPrefix("file") {
(get & path("text" / Remaining)) { fp =>
withoutSizeLimit {
complete(
HttpEntity(
ContentTypes.`application/octet-stream`,
fileStream("/users/tiger-macpro/" + fp, ))
)
}

注意:complete进行了HttpResponse的构建。因为Entity.dataByes就是Source[ByteString,_],所以我们可以直接把它导入Sink:

          entity.dataBytes.runWith(FileIO.toPath(Paths.get(destPath)))
.onComplete { case _ => println(s"Download file saved to: $destPath") }

上面我们提过FileIO.toPath就是一个Sink。由于我们的目的是大型的文件交换,所以无论上传下载都使用了withoutSizeLimit:

 val route = pathPrefix("file") {
(get & path("exchange" / Remaining)) { fp =>
withoutSizeLimit {
complete(
HttpEntity(
ContentTypes.`application/octet-stream`,
fileStream("/users/tiger-macpro/" + fp, ))
)
}
} ~
(post & path("exchange")) {
withoutSizeLimit {
extractDataBytes { bytes =>
val fut = bytes.runWith(FileIO.toPath(Paths.get(destPath)))
onComplete(fut) { _ =>
complete(s"Save upload file to: $destPath")
}
}
} }

好了下面的示范代码里对字符型或二进制文件都进行了交换的示范操作:

服务端:

import akka.actor._
import akka.stream._
import akka.stream.scaladsl._
import akka.http.scaladsl.Http
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.model._
import akka.http.scaladsl.model.HttpEntity._
import java.nio.file._ object FileServer extends App { implicit val httpSys = ActorSystem("httpSystem")
implicit val httpMat = ActorMaterializer()
implicit val httpEC = httpSys.dispatcher def fileStream(filePath: String, chunkSize: Int) = {
def loadFile = {
// implicit val ec = httpSys.dispatchers.lookup("akka.http.blocking-ops-dispatcher")
val file = Paths.get(filePath)
FileIO.fromPath(file, chunkSize)
.withAttributes(ActorAttributes.dispatcher("akka.http.blocking-ops-dispatcher"))
}
limitableByteSource(loadFile)
}
val destPath = "/users/tiger-macpro/downloads/A4-1.TIF"
val route = pathPrefix("file") {
(get & path("exchange" / Remaining)) { fp =>
withoutSizeLimit {
complete(
HttpEntity(
ContentTypes.`application/octet-stream`,
fileStream("/users/tiger-macpro/" + fp, ))
)
}
} ~
(post & path("exchange")) {
withoutSizeLimit {
extractDataBytes { bytes =>
val fut = bytes.runWith(FileIO.toPath(Paths.get(destPath)))
onComplete(fut) { _ =>
complete(s"Save upload file to: $destPath")
}
}
} }
} val (port, host) = (,"localhost") val bindingFuture = Http().bindAndHandle(route,host,port) println(s"Server running at $host $port. Press any key to exit ...") scala.io.StdIn.readLine() bindingFuture.flatMap(_.unbind())
.onComplete(_ => httpSys.terminate()) }

客户端:

import akka.actor._
import akka.stream._
import akka.stream.scaladsl._
import akka.http.scaladsl.Http
import akka.http.scaladsl.model.HttpEntity.limitableByteSource
import akka.http.scaladsl.model._
import java.nio.file._
import akka.util.ByteString
import scala.util._ object FileClient extends App { implicit val sys = ActorSystem("ClientSys")
implicit val mat = ActorMaterializer()
implicit val ec = sys.dispatcher def downloadFileTo(request: HttpRequest, destPath: String) = {
val futResp = Http(sys).singleRequest(request)
futResp
.andThen {
case Success(r@HttpResponse(StatusCodes.OK, _, entity, _)) =>
entity.dataBytes.runWith(FileIO.toPath(Paths.get(destPath)))
.onComplete { case _ => println(s"Download file saved to: $destPath") }
case Success(r@HttpResponse(code, _, _, _)) =>
println(s"Download request failed, response code: $code")
r.discardEntityBytes()
case Success(_) => println("Unable to download file!")
case Failure(err) => println(s"Download failed: ${err.getMessage}")
} } val dlFile = "Downloads/readme.txt"
val downloadText = HttpRequest(uri = s"http://localhost:8011/file/exchange/" + dlFile) downloadFileTo(downloadText, "/users/tiger-macpro/downloads/sample.txt")
scala.io.StdIn.readLine() val dlFile2 = "Downloads/image.png"
val downloadText2 = HttpRequest(uri = s"http://localhost:8011/file/exchange/" + dlFile2)
downloadFileTo(downloadText2, "/users/tiger-macpro/downloads/sample.png")
scala.io.StdIn.readLine() def uploadFile(request: HttpRequest, dataEntity: RequestEntity) = {
val futResp = Http(sys).singleRequest(
request.copy(entity = dataEntity)
)
futResp
.andThen {
case Success(r@HttpResponse(StatusCodes.OK, _, entity, _)) =>
entity.dataBytes.map(_.utf8String).runForeach(println)
case Success(r@HttpResponse(code, _, _, _)) =>
println(s"Upload request failed, response code: $code")
r.discardEntityBytes()
case Success(_) => println("Unable to Upload file!")
case Failure(err) => println(s"Upload failed: ${err.getMessage}") }
} def fileStream(filePath: String, chunkSize: Int): Source[ByteString,Any] = {
def loadFile = {
// implicit val ec = httpSys.dispatchers.lookup("akka.http.blocking-ops-dispatcher")
val file = Paths.get(filePath)
FileIO.fromPath(file, chunkSize)
.withAttributes(ActorAttributes.dispatcher("akka.http.blocking-ops-dispatcher"))
}
limitableByteSource(loadFile)
} val uploadText = HttpRequest(HttpMethods.POST,uri = s"http://localhost:8011/file/exchange")
val textData = HttpEntity(
ContentTypes.`application/octet-stream`,
fileStream("/Users/tiger-macpro/downloads/readme.txt",)
) uploadFile(uploadText,textData) scala.io.StdIn.readLine() sys.terminate() }

Akka(39): Http:File streaming-文件交换的更多相关文章

  1. MXF素材文件交换格式深入研究

    MXF素材文件交换格式深入研究   2012-09-03 | 访问次数:262 | 新闻来源:电科网               [摘要]DCI规定数字电影需采用MXF封装音视频等节目素材内容.为了深 ...

  2. JAVASE02-Unit06: 文件操作——File 、 文件操作—— RandomAccessFile

    Unit06: 文件操作--File . 文件操作-- RandomAccessFile java.io.FileFile的每一个实例是用来表示文件系统中的一个文件或目录 package day06; ...

  3. Hyper-V初涉_Hyper-V虚拟机文件交换

    使用虚拟机时,文件交互就显得十分重要.如果能实现物理机与虚拟机之间的文件交互,将会节省大量的时间.比较可惜的是,Hyper-V虚拟机并不支持USB存储设备,所以在文件交换上略显麻烦. 与Hyper-V ...

  4. java学习一目了然——File类文件处理

    java学习一目了然--File类文件处理 File类(java.io.File) 构造函数: File(String path) File(String parent,String child) F ...

  5. -1-4 java io java流 常用流 分类 File类 文件 字节流 字符流 缓冲流 内存操作流 合并序列流

      File类 •文件和目录路径名的抽象表示形式 构造方法 •public File(String pathname) •public File(String parent,Stringchild) ...

  6. 怎么给PDF文件交换页面

    在使用PDF文件的时候有文件页面的排版错误的时候,这个时候就需要交换页面了,那么怎么给PDF文件交换页面呢,在使用PDF文件的时候需要交换页面的时候要怎么做呢,下面小编就为大家分享一下PDF文件交换页 ...

  7. SQL Server ->> Sparse File(稀疏文件)

    Sparse File(稀疏文件)不是SQL Server的特性.它属于Windows的NTFS文件系统的一个特性.如果某个大文件中的数据包含着大量“0数据”(这个应该从二进制上看),这样的文件就可以 ...

  8. PF, Page File, 页面文件

    PF, Page File, 页面文件 是硬盘中用来当作内存使用的,仅仅提高物理内存可能导致CPU使用率高,因为降低了命中率: 学习了:https://baike.baidu.com/item/PF% ...

  9. Java IO_001.File类--文件或文件夹相关操作

    Java IO之File对象常用操作 File类:用于文件或文件夹或网址相关联的操作.可以关联或不关联文件(即关联不存在的文件).构造函数有: public File(String pathname) ...

随机推荐

  1. uva242,Stamps and Envelope Size

    这题紫薯上翻译错了 应该是:如果有多个并列,输出邮票种类最少的那个,如果还有并列,输出最大面值最小的那个 坑了我一个下午 dp[p][q]==1表示可以用不超过q张组成面额p 结合记忆化,p从1开始枚 ...

  2. PyCharm 2017 官网 下载 安装 设置 配置 (主题 字体 字号) 使用 入门 教程

    一.安装 Python 3.6 首先,要安装好 Python 3.6.如果你还没有安装,可以参考咪博士之前的教程 Python 3.6.3 官网 下载 安装 测试 入门教程 (windows) 二.官 ...

  3. 如何升级laravel5.4到laravel5.5并使用新特性?

    如何升级laravel5.4到laravel5.5并使用新特性? 修改composer.json: "laravel/framework": "5.5.*", ...

  4. JqueryMobile基础之创建页面

    首先简答介绍一下JQueryMobile吧,我觉得用一句话来讲就是可以 "写更少的代码,做更多的事情" : 它可以通过一个灵活及简单的方式来布局网页,且兼容所有移动设备.这也是我自 ...

  5. Vue源码后记-vFor列表渲染(3)

    这一节肯定能完! 经过DOM字符串的AST转化,再通过render变成vnode,最后就剩下patch到页面上了. render函数跑完应该是在这里: function mountComponent( ...

  6. ajax+php(环境搭建+测试)

    在学习JavaScript,jQuery的ajax部分的时候,一直不明白要如何进行,不知道服务器要怎么弄,什么都不知道,当初在学ajax的时候,除了看一下ajax的内容,实践极少,因为,不知道要怎么做 ...

  7. Lua 5.3 迭代器的简单示例

    Lua 5.3 迭代器的简单示例 创建"closure"模式的"iterator" function allowrds() local line = io.re ...

  8. Problem C

    Problem Description Here is a famous story in Chinese history. "That was about 2300 years ago. ...

  9. 关于php的命名空间

    php定义命名空间要使用namespace关键字,例:namespace Database 使用命名空间中的类要使用use关键字,也可以在use后面加as给类取别名,例:use Database\SQ ...

  10. scrollWidth,clientWidth,offsetWiddth,innerWinth 元素定位

    getBoundingClientRect()方法.它返回一个对象,其中包含了left.right.top.bottom四个属性,分别对应了该元素的左上角和右下角相对于浏览器窗口(viewport)左 ...