所谓文件交换指的是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. C#编译器和CLI的安装

    为了完成C#程序编译和运行,需要安装代码对应版本的编译器和CLI(公共语言框架)平台. (部分内容摘自<C#本质论>) 针对主流的CLI平台(Microsoft .NET),有两种安装方案 ...

  2. Mysql数据库之auto_increment

    一.概述 在数据库应用中,我们经常需要用到自动递增的唯一编号来标识记录.在MySQL中,可通过数据列的auto_increment属性来自动生成.可在建表时可用“auto_increment=n”选项 ...

  3. 树莓派.GPRS.短信接收器

    起因 曾经用过西门子出的短信猫, 好处是直接有SDK开发包, 不会硬件开发也能直接使用 缺点也是明显的, 就是只支持Windows系统, 另外就是在Windows下工作很不稳定, 隔开几天就会出现收不 ...

  4. LeetCode 342. Power of Four (4的次方)

    Given an integer (signed 32 bits), write a function to check whether it is a power of 4. Example:Giv ...

  5. spring mvc+mybatis+maven集成tkmapper+pagehelper

    <!-- maven tkmapper引入--> <dependency> <groupId>tk.mybatis</groupId> <arti ...

  6. poj 2345 Central heating

    Central heating Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 678   Accepted: 310 Des ...

  7. struts2常见错误

    1.Caught exception while loading file struts-default.xml - [unknown location]解决办法:由于lib包冲突造成的,将两个解析x ...

  8. css元素选择器 first-child nth-child

    E:first-child   只要E元素是它的父级的第一个子元素,就选中.它需要同时满足两个条件,    (1)是"第一个子元素",    (2)是"这个子元素刚好是E ...

  9. html5客户端本地存储之sessionStorage及storage事件

    首先您可以看一下<JavaScript本地存储实践(html5的localStorage和ie的userData)>sessionStorage和上文中提到的localStorage非常相 ...

  10. 关于Spring在多线程下的个人疑问

    在Web开发中,不可避免的是需要遇到并发操作的,并发操作就有可能会引发我们的多线程安全问题.比如说,我们多线程下访问同一个变量并且有一个线程做出修改那么就会使得我们另外的线程在不知情的情况下被修改自己 ...