restapi(1)- 文件上传下载服务
上次对restapi开了个头,设计了一个包括了身份验证和使用权限的restful服务开发框架。这是一个通用框架,开发人员只要直接往里面加新功能就行了。虽然这次的restapi是围绕着数据库表的CRUD操作设计的,但文件类数据在服务端与客户端之间的交换其实也很常用,特别是多媒体类如图片等文件类型。那我们就试着设计一个文件交换服务功能然后看看能不能很方便的加入到restapi框架内。
akka-http是以akka-stream为核心的,使用了大量的akka-stream功能。akka-stream中有一个FileIO组件库,里面提供了一系列有关文件读写功能,以数据流Source,Sink方式呈现:
...
def fromPath(f: Path, chunkSize: Int = ): Source[ByteString, Future[IOResult]] =
fromPath(f, chunkSize, startPosition = )
...
def toPath(
f: Path,
options: Set[OpenOption] = Set(WRITE, TRUNCATE_EXISTING, CREATE)): Sink[ByteString, Future[IOResult]] =
toPath(f, options, startPosition = )
可以发现,这些Source,Sink都是以ByteString为流元素进行操作的,akka-http自带了ByteString的Marshaller,可以实现数据格式自动转换,在网络传输中不需要增加什么数据格式转换动作。先用FileIO来产生一个Source[ByteString,_]:
package com.datatech.restapi
import akka.stream._
import akka.stream.scaladsl._
import java.nio.file._
import akka.util._
object FileStreaming { def fileStreamSource(filePath: String, chunkSize: Int = , dispatcherName: String = ""): Source[ ByteString,Any] = {
def loadFile = {
// implicit val ec = httpSys.dispatchers.lookup("akka.http.blocking-ops-dispatcher")
val file = Paths.get(filePath)
if (dispatcherName != "")
FileIO.fromPath(file, chunkSize)
.withAttributes(ActorAttributes.dispatcher(dispatcherName))
else
FileIO.fromPath(file, chunkSize)
}
loadFile
}
}
注意,我们可以用akka系统之外的线程池来进行FileIO操作,可以避免影响akka系统的运行效率。dispatcherName标注了在application.conf里自定义的线程池:
akka {
http {
blocking-ops-dispatcher {
type = Dispatcher
executor = "thread-pool-executor"
thread-pool-executor {
// or in Akka 2.4.2+
fixed-pool-size =
}
throughput =
}
}
}
下面是File功能架构FileRoute的设计:
package com.datatech.restapi
import akka.actor._
import akka.stream._
import akka.stream.scaladsl._
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.model._
import akka.http.scaladsl.coding.Gzip
import java.nio.file._
import FileStreaming._
import AuthBase._ case class FileRoute(jwt: String)(implicit auth: AuthBase, sys: ActorSystem) { val destPath = "/users/tiger-macpro/cert4/meme.jpg"
implicit val mat = ActorMaterializer()
val route = pathPrefix("file") {
val privatePath = auth.tempDirFromJwt(jwt)
if (privatePath.length == )
complete(StatusCodes.NotFound) (get & path(Remaining)) { filename =>
withoutSizeLimit {
encodeResponseWith(Gzip) {
complete(
HttpEntity(
ContentTypes.`application/octet-stream`,
fileStreamSource(privatePath+"/download/"+filename, ))
)
}
}
} ~
(post & parameters('filename)) { filename =>
withoutSizeLimit {
decodeRequest {
extractDataBytes { bytes =>
val fut = bytes.runWith(FileIO.toPath(Paths.get(privatePath+"/upload/"+filename)))
onComplete(fut) { _ => complete(StatusCodes.OK)}
}
}
} } }
}
每个用户在服务端都应该有个独立的文件目录,这个刚好可以放在jwt里:
package com.datatech.restapi
import akka.http.scaladsl.server.directives.Credentials
import AuthBase._
object MockUserAuthService { case class User(username: String, password: String, userInfo: UserInfo)
val validUsers = Seq(User("johnny", "p4ssw0rd",
Map("shopid" -> "", "userid" -> "", "tmpdir" ->"/users/tiger-macpro/1101101"))
,User("tiger", "secret",
Map("shopid" -> "" , "userid" -> "", "tmpdir" ->"/users/tiger-macpro/1101102"))) def getValidUser(credentials: Credentials): Option[UserInfo] =
credentials match {
case p @ Credentials.Provided(_) =>
validUsers.find(user => user.username == p.identifier && p.verify(user.password)) match {
case Some(user) => Some(user.userInfo)
case _ => None
}
case _ => None
} }
个人目录tmpdir是放在UserInfo里的,我们只需要从jwt里解析分离出来:
def tempDirFromJwt(jwt: String): String = {
val optUserInfo = getUserInfo(jwt)
val dir: String = optUserInfo match {
case Some(m) =>
try {
m("tmpdir").toString
} catch {case err: Throwable => ""}
case None => ""
}
dir
}
文件交换服务是需要使用权限的,所以FileRoute要放在authenticateOAuth2下面:
val route =
path("auth") {
authenticateBasic(realm = "auth", authenticator.getUserInfo) { userinfo =>
post { complete(authenticator.issueJwt(userinfo))}
}
} ~
pathPrefix("api") {
authenticateOAuth2(realm = "api", authenticator.authenticateToken) { validToken =>
(path("hello") & get) {
complete(s"Hello! userinfo = ${authenticator.getUserInfo(validToken)}")
} ~
(path("how are you") & get) {
complete(s"Hello! userinfo = ${authenticator.getUserInfo(validToken)}")
} ~
FileRoute(validToken)
.route
// ~ ...
}
}
写一个客户端来测试文件交换服务:
import akka.stream._
import java.nio.file._
import java.io._
import akka.http.scaladsl.model.headers._
import scala.concurrent._
import com.datatech.restapi.FileStreaming._
import scala.concurrent.duration._
import akka.actor.ActorSystem
import akka.http.scaladsl.marshalling.Marshal
import akka.http.scaladsl.model._
import akka.http.scaladsl.Http
import akka.stream.scaladsl.{FileIO, Source}
import scala.util._ case class FileUtil(implicit sys: ActorSystem) {
import sys.dispatcher
implicit val mat = ActorMaterializer()
def createEntity(file: File): RequestEntity = {
require(file.exists())
val formData =
Multipart.FormData(
Source.single(
Multipart.FormData.BodyPart(
"test",
HttpEntity(MediaTypes.`application/octet-stream`, file.length(), FileIO.fromPath(file.toPath, chunkSize = )), // the chunk size here is currently critical for performance
Map("filename" -> file.getName))))
Await.result(Marshal(formData).to[RequestEntity], seconds)
} def uploadFile(request: HttpRequest, dataEntity: RequestEntity) = {
implicit val mat = ActorMaterializer()
import sys.dispatcher
val futResp = Http(sys).singleRequest(
// Gzip.encodeMessage(
request.copy(entity = dataEntity) //.addHeader(`Content-Encoding`(HttpEncodings.gzip))
// )
)
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 downloadFileTo(request: HttpRequest, destPath: String) = {
// val req = request.addHeader(`Content-Encoding`(HttpEncodings.gzip))
val futResp = Http(sys).singleRequest(request) //.map(Gzip.decodeMessage(_))
futResp
.andThen {
case Success(r@HttpResponse(StatusCodes.OK, _, entity, _)) =>
entity.withoutSizeLimit().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}")
} } } object TestFileClient {
type UserInfo = Map[String,Any]
def main(args: Array[String]): Unit = {
implicit val system = ActorSystem()
implicit val materializer = ActorMaterializer()
// needed for the future flatMap/onComplete in the end
implicit val executionContext = system.dispatcher val helloRequest = HttpRequest(uri = "http://192.168.11.189:50081/") val authorization = headers.Authorization(BasicHttpCredentials("johnny", "p4ssw0rd"))
val authRequest = HttpRequest(
HttpMethods.POST,
uri = "http://192.168.11.189:50081/auth",
headers = List(authorization)
) val futToken: Future[HttpResponse] = Http().singleRequest(authRequest) val respToken = for {
resp <- futToken
jstr <- resp.entity.dataBytes.runFold("") {(s,b) => s + b.utf8String}
} yield jstr val jstr = Await.result[String](respToken, seconds)
println(jstr) scala.io.StdIn.readLine() val authentication = headers.Authorization(OAuth2BearerToken(jstr)) val entity = HttpEntity(
ContentTypes.`application/octet-stream`,
fileStreamSource("/Users/tiger-macpro/cert3/ctiger.jpg",)
)
//
val chunked = HttpEntity.Chunked.fromData(
ContentTypes.`application/octet-stream`,
fileStreamSource("/Users/tiger-macpro/cert3/ctiger.jpg",)
) val multipart = FileUtil().createEntity(new File("/Users/tiger-macpro/cert3/ctiger.jpg")) val uploadRequest = HttpRequest(
HttpMethods.POST,
uri = "http://192.168.11.189:50081/api/file?filename=mypic.jpg",
).addHeader(authentication) //upload file
//Await.ready(FileUtil().uploadFile(uploadRequest,entity),2 seconds)
//Await.ready(FileUtil().uploadFile(uploadRequest,chunked),2 seconds)
Await.ready(FileUtil().uploadFile(uploadRequest,multipart), seconds) val dlRequest = HttpRequest(
HttpMethods.GET,
uri = "http://192.168.11.189:50081/api/file/mypic.jpg",
).addHeader(authentication) FileUtil().downloadFileTo(dlRequest, "/users/tiger-macpro/cert3/mypic.jpg") scala.io.StdIn.readLine()
system.terminate()
} }
在文件上传upload时试过用entity,chunked,multipart方式构建的request-entity,服务端都能处理。好像看过很多java的httpclient图片上传,都是用multipart entity。现在这个服务端能正确处理。当然,在服务端同样可以用multipart方式提供文件下载服务,就不在这里实现了。不过可以提供一段示范代码:
import akka.actor._
import akka.stream._
import java.nio.file._
import java.io._ import akka.http.scaladsl.unmarshalling.Unmarshal
import akka.util.ByteString import scala.concurrent.duration._
import akka.actor.ActorSystem
import akka.http.scaladsl.Http.ServerBinding
import akka.http.scaladsl.marshalling.Marshal
import akka.http.scaladsl.model._
import akka.http.scaladsl.server.Route
import akka.http.scaladsl.Http
import akka.stream.scaladsl.{FileIO, Source}
import com.typesafe.config.{Config, ConfigFactory} import scala.concurrent.Future object TestMultipartFileUpload extends App {
val testConf: Config = ConfigFactory.parseString("""
akka.loglevel = INFO
akka.log-dead-letters = off""")
implicit val system = ActorSystem("ServerTest", testConf)
import system.dispatcher
implicit val materializer = ActorMaterializer() val testFile: File = new File("/users/tiger-macpro/downloads/uploadFileDemo.scala") //args(0)) def startTestServer(): Future[ServerBinding] = {
import akka.http.scaladsl.server.Directives._ val route: Route =
path("upload") {
entity(as[Multipart.FormData]) { (formdata: Multipart.FormData) ⇒ val fileNamesFuture = formdata.parts.mapAsync() { p ⇒
println(s"Got part. name: ${p.name} filename: ${p.filename}") println("Counting size...")
@volatile var lastReport = System.currentTimeMillis()
@volatile var lastSize = 0L
def receiveChunk(counter: (Long, Long), chunk: ByteString): (Long, Long) = {
val (oldSize, oldChunks) = counter
val newSize = oldSize + chunk.size
val newChunks = oldChunks + val now = System.currentTimeMillis()
if (now > lastReport + ) {
val lastedTotal = now - lastReport
val bytesSinceLast = newSize - lastSize
val speedMBPS = bytesSinceLast.toDouble / /* bytes per MB */ / lastedTotal * /* millis per second */ println(f"Already got $newChunks%7d chunks with total size $newSize%11d bytes avg chunksize ${newSize / newChunks}%7d bytes/chunk speed: $speedMBPS%6.2f MB/s") lastReport = now
lastSize = newSize
}
(newSize, newChunks)
} p.entity.dataBytes.runFold((0L, 0L))(receiveChunk).map {
case (size, numChunks) ⇒
println(s"Size is $size")
(p.name, p.filename, size)
}
}.runFold(Seq.empty[(String, Option[String], Long)])(_ :+ _).map(_.mkString(", ")) complete {
fileNamesFuture
}
}
}
Http().bindAndHandle(route, interface = "localhost", port = )
} def createEntity(file: File): Future[RequestEntity] = {
require(file.exists())
val formData =
Multipart.FormData(
Source.single(
Multipart.FormData.BodyPart(
"test",
HttpEntity(MediaTypes.`application/octet-stream`, file.length(), FileIO.fromPath(file.toPath, chunkSize = )), // the chunk size here is currently critical for performance
Map("filename" -> file.getName))))
Marshal(formData).to[RequestEntity]
} def createRequest(target: Uri, file: File): Future[HttpRequest] =
for {
e ← createEntity(file)
} yield HttpRequest(HttpMethods.POST, uri = target, entity = e) try {
val result =
for {
ServerBinding(address) ← startTestServer()
_ = println(s"Server up at $address")
port = address.getPort
target = Uri(scheme = "http", authority = Uri.Authority(Uri.Host("localhost"), port = port), path = Uri.Path("/upload"))
req ← createRequest(target, testFile)
_ = println(s"Running request, uploading test file of size ${testFile.length} bytes")
response ← Http().singleRequest(req)
responseBodyAsString ← Unmarshal(response).to[String]
} yield responseBodyAsString result.onComplete { res ⇒
println(s"The result was $res")
system.terminate()
} system.scheduler.scheduleOnce(.seconds) {
println("Shutting down after timeout...")
system.terminate()
}
} catch {
case _: Throwable ⇒ system.terminate()
}
}
上面这个示范里包括了服务端,客户端对multipart的数据处理。
在上面这个例子里我们先设计了一个独立的包括文件交换服务功能的FileRoute类,然后直接把FileRoute.route贴在主菜单后面就完成了文件交换服务功能的添加。比较接近实现restapi设计初衷。
下面是本次示范源代码:
build.sbt
name := "restapi" version := "0.1" scalaVersion := "2.12.8" libraryDependencies ++= Seq(
"com.typesafe.akka" %% "akka-http" % "10.1.8",
"com.typesafe.akka" %% "akka-stream" % "2.5.23",
"com.pauldijou" %% "jwt-core" % "3.0.1",
"de.heikoseeberger" %% "akka-http-json4s" % "1.22.0",
"org.json4s" %% "json4s-native" % "3.6.1",
"com.typesafe.akka" %% "akka-http-spray-json" % "10.1.8",
"com.typesafe.scala-logging" %% "scala-logging" % "3.9.0",
"org.slf4j" % "slf4j-simple" % "1.7.25",
"org.json4s" %% "json4s-jackson" % "3.6.7",
"org.json4s" %% "json4s-ext" % "3.6.7"
)
auth/AuthBase.scala
package com.datatech.restapi import akka.http.scaladsl.server.directives.Credentials
import pdi.jwt._
import org.json4s.native.Json
import org.json4s._
import org.json4s.jackson.JsonMethods._
import pdi.jwt.algorithms._
import scala.util._ object AuthBase {
type UserInfo = Map[String, Any]
case class AuthBase(
algorithm: JwtAlgorithm = JwtAlgorithm.HMD5,
secret: String = "OpenSesame",
getUserInfo: Credentials => Option[UserInfo] = null) {
ctx =>
def withAlgorithm(algo: JwtAlgorithm): AuthBase = ctx.copy(algorithm=algo)
def withSecretKey(key: String): AuthBase = ctx.copy(secret = key)
def withUserFunc(f: Credentials => Option[UserInfo]): AuthBase = ctx.copy(getUserInfo = f) def authenticateToken(credentials: Credentials): Option[String] =
credentials match {
case Credentials.Provided(token) =>
algorithm match {
case algo: JwtAsymmetricAlgorithm =>
Jwt.isValid(token, secret, Seq((algorithm.asInstanceOf[JwtAsymmetricAlgorithm]))) match {
case true => Some(token)
case _ => None
}
case _ =>
Jwt.isValid(token, secret, Seq((algorithm.asInstanceOf[JwtHmacAlgorithm]))) match {
case true => Some(token)
case _ => None
}
}
case _ => None
} def getUserInfo(token: String): Option[UserInfo] = {
algorithm match {
case algo: JwtAsymmetricAlgorithm =>
Jwt.decodeRawAll(token, secret, Seq(algorithm.asInstanceOf[JwtAsymmetricAlgorithm])) match {
case Success(parts) => Some(((parse(parts._2).asInstanceOf[JObject]) \ "userinfo").values.asInstanceOf[UserInfo])
case Failure(err) => None
}
case _ =>
Jwt.decodeRawAll(token, secret, Seq(algorithm.asInstanceOf[JwtHmacAlgorithm])) match {
case Success(parts) => Some(((parse(parts._2).asInstanceOf[JObject]) \ "userinfo").values.asInstanceOf[UserInfo])
case Failure(err) => None
}
}
} def issueJwt(userinfo: UserInfo): String = {
val claims = JwtClaim() + Json(DefaultFormats).write(("userinfo", userinfo))
Jwt.encode(claims, secret, algorithm)
} def tempDirFromJwt(jwt: String): String = {
val optUserInfo = getUserInfo(jwt)
val dir: String = optUserInfo match {
case Some(m) =>
try {
m("tmpdir").toString
} catch {case err: Throwable => ""}
case None => ""
}
dir
} } }
file/FileStreaming.scala
package com.datatech.restapi
import akka.stream._
import akka.stream.scaladsl._
import java.nio.file._
import akka.util._
object FileStreaming { def fileStreamSource(filePath: String, chunkSize: Int = , dispatcherName: String = ""): Source[ ByteString,Any] = {
def loadFile = {
// implicit val ec = httpSys.dispatchers.lookup("akka.http.blocking-ops-dispatcher")
val file = Paths.get(filePath)
if (dispatcherName != "")
FileIO.fromPath(file, chunkSize)
.withAttributes(ActorAttributes.dispatcher(dispatcherName))
else
FileIO.fromPath(file, chunkSize)
}
loadFile
}
}
file/FileRoute.scala
package com.datatech.restapi
import akka.actor._
import akka.stream._
import akka.stream.scaladsl._
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.model._
import akka.http.scaladsl.coding.Gzip
import java.nio.file._
import FileStreaming._
import AuthBase._ case class FileRoute(jwt: String)(implicit auth: AuthBase, sys: ActorSystem) { val destPath = "/users/tiger-macpro/cert4/meme.jpg"
implicit val mat = ActorMaterializer()
val route = pathPrefix("file") {
val privatePath = auth.tempDirFromJwt(jwt)
if (privatePath.length == )
complete(StatusCodes.NotFound) (get & path(Remaining)) { filename =>
withoutSizeLimit {
encodeResponseWith(Gzip) {
complete(
HttpEntity(
ContentTypes.`application/octet-stream`,
fileStreamSource(privatePath+"/download/"+filename, ))
)
}
}
} ~
(post & parameters('filename)) { filename =>
withoutSizeLimit {
decodeRequest {
extractDataBytes { bytes =>
val fut = bytes.runWith(FileIO.toPath(Paths.get(privatePath+"/upload/"+filename)))
onComplete(fut) { _ => complete(StatusCodes.OK)}
}
}
} } }
}
MockUserAuthService.scala
package com.datatech.restapi
import akka.http.scaladsl.server.directives.Credentials
import AuthBase._
object MockUserAuthService { case class User(username: String, password: String, userInfo: UserInfo)
val validUsers = Seq(User("johnny", "p4ssw0rd",
Map("shopid" -> "", "userid" -> "", "tmpdir" ->"/users/tiger-macpro/1101101"))
,User("tiger", "secret",
Map("shopid" -> "" , "userid" -> "", "tmpdir" ->"/users/tiger-macpro/1101102"))) def getValidUser(credentials: Credentials): Option[UserInfo] =
credentials match {
case p @ Credentials.Provided(_) =>
validUsers.find(user => user.username == p.identifier && p.verify(user.password)) match {
case Some(user) => Some(user.userInfo)
case _ => None
}
case _ => None
} }
RestApiServer.scala
package com.datatech.restapi import akka.actor._
import akka.stream._
import akka.http.scaladsl.Http
import akka.http.scaladsl.server.Directives._
import pdi.jwt._
import AuthBase._
import MockUserAuthService._ object RestApiServer extends App { implicit val httpSys = ActorSystem("httpSystem")
implicit val httpMat = ActorMaterializer()
implicit val httpEC = httpSys.dispatcher implicit val authenticator = new AuthBase()
.withAlgorithm(JwtAlgorithm.HS256)
.withSecretKey("OpenSesame")
.withUserFunc(getValidUser) val route =
path("auth") {
authenticateBasic(realm = "auth", authenticator.getUserInfo) { userinfo =>
post { complete(authenticator.issueJwt(userinfo))}
}
} ~
pathPrefix("api") {
authenticateOAuth2(realm = "api", authenticator.authenticateToken) { validToken =>
(path("hello") & get) {
complete(s"Hello! userinfo = ${authenticator.getUserInfo(validToken)}")
} ~
(path("how are you") & get) {
complete(s"Hello! userinfo = ${authenticator.getUserInfo(validToken)}")
} ~
FileRoute(validToken)
.route
// ~ ...
}
} val (port, host) = (,"192.168.11.189") 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()) }
TestFileClient.scala
import akka.stream._
import java.nio.file._
import java.io._
import akka.http.scaladsl.model.headers._
import scala.concurrent._
import com.datatech.restapi.FileStreaming._
import scala.concurrent.duration._
import akka.actor.ActorSystem
import akka.http.scaladsl.marshalling.Marshal
import akka.http.scaladsl.model._
import akka.http.scaladsl.Http
import akka.stream.scaladsl.{FileIO, Source}
import scala.util._ case class FileUtil(implicit sys: ActorSystem) {
import sys.dispatcher
implicit val mat = ActorMaterializer()
def createEntity(file: File): RequestEntity = {
require(file.exists())
val formData =
Multipart.FormData(
Source.single(
Multipart.FormData.BodyPart(
"test",
HttpEntity(MediaTypes.`application/octet-stream`, file.length(), FileIO.fromPath(file.toPath, chunkSize = )), // the chunk size here is currently critical for performance
Map("filename" -> file.getName))))
Await.result(Marshal(formData).to[RequestEntity], seconds)
} def uploadFile(request: HttpRequest, dataEntity: RequestEntity) = {
implicit val mat = ActorMaterializer()
import sys.dispatcher
val futResp = Http(sys).singleRequest(
// Gzip.encodeMessage(
request.copy(entity = dataEntity) //.addHeader(`Content-Encoding`(HttpEncodings.gzip))
// )
)
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 downloadFileTo(request: HttpRequest, destPath: String) = {
// val req = request.addHeader(`Content-Encoding`(HttpEncodings.gzip))
val futResp = Http(sys).singleRequest(request) //.map(Gzip.decodeMessage(_))
futResp
.andThen {
case Success(r@HttpResponse(StatusCodes.OK, _, entity, _)) =>
entity.withoutSizeLimit().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}")
} } } object TestFileClient {
type UserInfo = Map[String,Any]
def main(args: Array[String]): Unit = {
implicit val system = ActorSystem()
implicit val materializer = ActorMaterializer()
// needed for the future flatMap/onComplete in the end
implicit val executionContext = system.dispatcher val helloRequest = HttpRequest(uri = "http://192.168.11.189:50081/") val authorization = headers.Authorization(BasicHttpCredentials("johnny", "p4ssw0rd"))
val authRequest = HttpRequest(
HttpMethods.POST,
uri = "http://192.168.11.189:50081/auth",
headers = List(authorization)
) val futToken: Future[HttpResponse] = Http().singleRequest(authRequest) val respToken = for {
resp <- futToken
jstr <- resp.entity.dataBytes.runFold("") {(s,b) => s + b.utf8String}
} yield jstr val jstr = Await.result[String](respToken, seconds)
println(jstr) scala.io.StdIn.readLine() val authentication = headers.Authorization(OAuth2BearerToken(jstr)) val entity = HttpEntity(
ContentTypes.`application/octet-stream`,
fileStreamSource("/Users/tiger-macpro/cert3/ctiger.jpg",)
)
//
val chunked = HttpEntity.Chunked.fromData(
ContentTypes.`application/octet-stream`,
fileStreamSource("/Users/tiger-macpro/cert3/ctiger.jpg",)
) val multipart = FileUtil().createEntity(new File("/Users/tiger-macpro/cert3/ctiger.jpg")) val uploadRequest = HttpRequest(
HttpMethods.POST,
uri = "http://192.168.11.189:50081/api/file?filename=mypic.jpg",
).addHeader(authentication) //upload file
//Await.ready(FileUtil().uploadFile(uploadRequest,entity),2 seconds)
//Await.ready(FileUtil().uploadFile(uploadRequest,chunked),2 seconds)
Await.ready(FileUtil().uploadFile(uploadRequest,multipart), seconds) val dlRequest = HttpRequest(
HttpMethods.GET,
uri = "http://192.168.11.189:50081/api/file/mypic.jpg",
).addHeader(authentication) FileUtil().downloadFileTo(dlRequest, "/users/tiger-macpro/cert3/mypic.jpg") scala.io.StdIn.readLine()
system.terminate()
} }
restapi(1)- 文件上传下载服务的更多相关文章
- centos 6.5下安装文件上传下载服务
centos 6.5下安装文件上传下载服务 由于每次在CentOS中要下载一些配置文件到物理机,和上传一些文件到服务器,导致来回的开启ftp软件有点麻烦,这里我们可以使用文件上传下载服务,来解决上传和 ...
- Nginx + Lua搭建文件上传下载服务
收录待用,修改转载已取得腾讯云授权 最新腾讯云技术公开课直播,提问腾讯W3C代表,如何从小白成为技术专家?点击了解活动详情 作者 | 庄进发 编辑 | 迷鹿 庄进发,信息安全部后台开发工程师,主要负责 ...
- WebService实现文件上传下载
一:服务端:一个普通java web工程 package com.wzh.file; import com.sun.xml.ws.developer.StreamingAttachment; impo ...
- webservice文件上传下载
使用DataHandler实现webservice的文件上传下载 服务端代码: package com.hello.weChat.controller; import javax.activation ...
- 2013第38周日Java文件上传下载收集思考
2013第38周日Java文件上传&下载收集思考 感觉文件上传及下载操作很常用,之前简单搜集过一些东西,没有及时学习总结,现在基本没啥印象了,今天就再次学习下,记录下自己目前知识背景下对该类问 ...
- HttpClient文件上传下载
1 HTTP HTTP 协议可能是如今 Internet 上使用得最多.最重要的协议了,越来越多的 Java 应用程序须要直接通过 HTTP 协议来訪问网络资源. 尽管在 JDK 的 java.net ...
- Hadoop之HDFS原理及文件上传下载源码分析(下)
上篇Hadoop之HDFS原理及文件上传下载源码分析(上)楼主主要介绍了hdfs原理及FileSystem的初始化源码解析, Client如何与NameNode建立RPC通信.本篇将继续介绍hdfs文 ...
- .Net Core 图片文件上传下载
当下.Net Core项目可是如雨后春笋一般发展起来,作为.Net大军中的一员,我热忱地拥抱了.Net Core并且积极使用其进行业务的开发,我们先介绍下.Net Core项目下实现文件上传下载接口. ...
- 阿里云负载均衡SLB的文件上传下载问题解决
Nfs同步文件夹配置 问题描述 : javaweb应用部署到云服务器上时,当服务器配置了SLB负载均衡的时候,多台服务器就会造成文件上传下载获取不到文件的错误, 解决办法有:1.hdfs 2.搭建f ...
随机推荐
- UWP SQLite的使用
原文:UWP SQLite的使用 1.准备工作 1.首先我们要给项目添加"SQLite for Universal Windows Platform"扩展 点击菜单栏的" ...
- JavaScript语言核心--词法结构
编程语言的词法结构是一套基础性规则,用来描述如何使用这门语言来编写程序.作为语法的基础,它规定了诸如变量名是什么样的.怎么写注释,以及程序语言之间如何分隔等规则. 1. 字符集 JavaScript程 ...
- 分布式存储系统GlusterFS初体验
摘要: GlusterFS是Scale-Out存储解决方案Gluster的核心,它是一个开源的分布式文件系统,具有强大的横向扩展能力,通过扩展能够支持数PB存储容量和处理数千客户端.GlusterFS ...
- javascript 实现ajax
AJAX 英文名称 Asynchronous JavaScript and XML即异步的 JavaScript 和 XML AJAX 是与服务器交换数据并更新部分网页一门无刷新技术构建自己的ajax ...
- TopFreeTheme精选免费模板【20130626】
有一段时间没有发布的模板了,相信很多喜欢新模板的朋友有点焦急了!还好,今天我今天整理了13个最新的模板,主要是WordPress的,另外3个是关于Joomla的模板,他们分别是游戏主题.俱乐部主题以及 ...
- [2017.02.21-22] 《Haskell趣学指南 —— Learning You a Haskell for Great Good!》
{- 2017.02.21-22 <Haskell趣学指南 -- Learning You a Haskell for Great Good!> 学习了Haskell的基本语法,并实现了一 ...
- linux oracle 启动全过程
一:启动oracle [root@ccoracle ~]# su -l oracle [oracle@ccoracle ~]$ sqlplus /nolog SQL*Plus: Release 10. ...
- SYN012型B码时统
SYN012型B码时统 产品概述 SYN012型B码时统是由西安同步电子科技有限公司精心设计.自行研发生产的一款通用性时统终端,内置高精度恒温晶振,接收GPS北斗双模卫星信号,10MHz外部参考 ...
- ZooKeeper学习第一期---Zookeeper简单介绍(转)
转载来源:https://www.cnblogs.com/sunddenly/p/4033574.html 一.分布式协调技术 在给大家介绍ZooKeeper之前先来给大家介绍一种技术--分布式协调技 ...
- 高性能嵌入式核心板新标杆!米尔推出基于NXP i.MX8M处理器的MYC-JX8MX核心板
随着嵌入式及物联网技术的飞速发展,高性能计算的嵌入式板卡已经成为智能产品的基础硬件平台.为响应行业应用和满足客户需求,米尔电子推出基于NXP公司i.MX8M系列芯片的开发平台MYD-JX8MX系列开发 ...