restapi(5)- rest-mongo 应用实例:分布式图片管理系统之一,rest 服务
最近有同事提起想把网页上的图片存在MongoDB里,我十分赞同。比起把图片以文件形式存放在硬盘子目录的方式,MongoDB有太多的优势。首先,MongoDB是分布式数据库,图片可以跨服务器存储。在一个集群环境里通过复制集、分片等技术可以提高图片读取速度、实现数据的高可用和安全性。再就是对大量的图片可用规范的记录管理方式来进行处理,甚至在一个大流量环境里还可以用集群节点负载平衡方式来助力图片的存取。
我想了想看有没有办法让这个图片管理系统尽用分布式集群软件的能力。MongoDB是一个分布式数据库,在一个集群内任何节点都可以存取,也就是说在集群所有节点上都部署统一的rest-mongo,这样客户端可以用不同的ip地址来访问不同的节点提交图片存取请求。假设某一个节点接待比别的节点更多的客户端,那么我们可以把图片存取的过程放到其它比较空闲的节点上去运行,即所谓负载均衡了。看来这个系统需要MongoDB,rest-mongo和akka-cluster这几个组件。
我们先从前端需求开始:页面上每个商品有n个图片,客户端提出存入系统请求时提供商品编号、描述、默认尺寸及图片。对一个商品提出n个存写请求,同一个商品编号,系统对每张图片自动产生序号并在httprespose中返回给客户端。客户端取图片时提供商品编号,系统先把这个商品的所有图片序号返还客户端,客户端再按序号一张一张索取图片,并指定输出图片的伸缩尺寸。
这篇我们先跟着前几篇的内容把有关图片存取的rest服务实现了。在上篇rest-mongo的基础上,针对新的系统需求做一些针对性的修改应该就行了。
首先是Model部分,如下:
case class WebPic(
pid: String,
seqno: Int,
desc: Option[String],
width: Option[Int],
heigth: Option[Int],
pic: Option[MGOBlob]
) extends ModelBase[Document] { self =>
override def to: Document = {
var doc = Document(
"pid" -> self.pid,
"seqno" -> self.seqno
)
if (self.desc != None)
doc = doc + ("desc" -> self.desc.get)
if (self.width != None)
doc = doc + ("width" -> self.width.get)
if (self.heigth != None)
doc = doc + ("heigth" -> self.heigth.get)
if (self.pic != None)
doc = doc + ("photo" -> self.pic.get)
doc
}
}
object WebPic {
def fromDocument: Document => WebPic = doc => {
WebPic(
pid = doc.getString("pid"),
seqno = doc.getInteger("seqno"),
desc = mgoGetStringOrNone(doc,"desc"),
width = mgoGetIntOrNone(doc,"width").asInstanceOf[Option[Int]],
heigth = mgoGetIntOrNone(doc,"heigth").asInstanceOf[Option[Int]],
pic = None
)
}
}
width,height字段是客户端提供的默认宽高尺寸。如果客户在请求图片时没有提供就用数据库里客户端在提交存储时提供的默认宽高。
在repo里还要增加一个count功能,提供一个pid, 返回在该pid名下存写的图片数量:
import org.mongodb.scala.model.Filters._
def count(pid: String):DBOResult[Int] = {
val ctxCount = MGOContext(dbName = db,collName=coll)
.setActionType(MGO_ACTION_TYPE.MGO_QUERY)
.setCommand(Count(filter=Some(equal("pid",pid))))
mgoQuery[Int](ctxCount,None)
}
返回类型是DBResult[Int]。还要加一个读取第一条WebPic记录的功能:
def getOnePicture(pid: String, seqno: Int): DBOResult[R] = {
val ctxFind = MGOContext(dbName = db, collName = coll)
.setActionType(MGO_ACTION_TYPE.MGO_QUERY)
.setCommand(Find(filter = Some(and(equal("pid",pid),equal("seqno",seqno))), firstOnly = true))
mgoQuery[R](ctxFind, converter)
}
注意这个函数返回的是DBOResult[R]类型。这是因为我们需要把整条记录读出来,特别是width,height字段,方便在用户没有指定宽高时提供默认值。因为涉及到具体的字段名称,所以要在读出Document时做一个WebPic转换:
def getOneDocument(filtr: Bson): DBOResult[Document] = {
val ctxFind = MGOContext(dbName = db,collName=coll)
.setActionType(MGO_ACTION_TYPE.MGO_QUERY)
.setCommand(Find(filter = Some(filtr),firstOnly = true))
mgoQuery[Document](ctxFind,None)
}
def getOnePicture(pid: String, seqno: Int): DBOResult[R] = {
val ctxFind = MGOContext(dbName = db, collName = coll)
.setActionType(MGO_ACTION_TYPE.MGO_QUERY)
.setCommand(Find(filter = Some(and(equal("pid",pid),equal("seqno",seqno))), firstOnly = true))
mgoQuery[R](ctxFind, converter)
}
要用getOnPicture, getOnDocument是通用的。在编译时无法识别width,height。
好了,下面是Route部分的修改。先从用户提交图片存储请求开始,用户可能用下面这样格式的url来请求:
(post & parameters('pid,'desc.?,'width.as[Int].?,'heigth.as[Int].?)) 如:
http://example.com:50081/public/gms/pictures?pid=apple&width=128 图片放在HttpRequest的Entity里面。
我们需要先获取apple的数量seqno、把信息存入数据库然后返回这个seqno:
pathPrefix("pictures") {
(post & parameters('pid,'desc.?,'width.as[Int].?,'heigth.as[Int].?)) { (pid, optDesc, optWid, optHgh) =>
val futCount: Future[Int] = repository.count(pid).value.value.runToFuture.map {
eoi =>
eoi match {
case Right(oi) => oi match {
case Some(i) => i
case None => -
}
case Left(err) => -
}
}
val count: Int = Await.result(futCount, seconds)
var doc = Document(
"pid" -> pid,
"seqno" -> count
)
if (optDesc != None)
doc = doc + ("desc" -> optDesc.get)
if (optWid != None)
doc = doc + ("desc" -> optWid.get)
if (optHgh != None)
doc = doc + ("desc" -> optHgh.get) withoutSizeLimit {
decodeRequest {
extractDataBytes { bytes =>
val fut = bytes.runFold(ByteString()) { case (hd, bs) =>
hd ++ bs
}
onComplete(fut) {
case Success(b) =>
doc = doc + ("pic" -> b.toArray)
val futmsg: Future[String] = repository.insert(doc).value.value.runToFuture.map {
eoc =>
eoc match {
case Right(oc) => oc match {
case Some(c) => count.toString // c.toString()
case None => "insert may not complete!"
}
case Left(err) => err.getMessage
}
}
complete(futmsg)
case Failure(err) => complete(err)
}
}
}
}
注意,在Response里的返回结果一定是ByteString类型的。
图片读取请求分两步:先提供pid获取一个不含图片的记录清单(注意Model里WebPic的fromDocument函数里pic=None),返还用户,如:http://example.com:50081/public/pms/pictures?pid=apple
用户得到清单里的seqno后组装成完整url:http://example.com:50081/public/pms/pictures?pid=apple&seqno=2&height=64
系统读取图片并按用户关于宽高要求或数据库里默认宽高数据输出图片:
(get & parameters('pid, 'seqno.as[Int].?,'width.as[Int].?,'height.as[Int].?)) {
(pid, optSeq, optWid,optHght) =>
if (optSeq == None) {
dbor = repository.query(equal("pid", pid))
val futRows = dbor.value.value.runToFuture.map {
eolr =>
eolr match {
case Right(olr) => olr match {
case Some(lr) => lr
case None => Seq[M]()
}
case Left(_) => Seq[M]()
}
}
complete(futureToJson(futRows))
} else {
val futOptPicRow: CancelableFuture[Option[WebPic]] = repository.getOnePicture(pid,optSeq.get)
.value.value.runToFuture.map {
eorow =>
eorow match {
case Right(orow) => orow match {
case Some(row) =>
if (row == null) None
else Some(row.asInstanceOf[WebPic])
case None => None
}
case Left(_) => None
}
}
onComplete(futOptPicRow) {
case Success(optRow) => optRow match {
case Some(row) =>
val width = if(optWid == None) row.width.getOrElse() else optWid.getOrElse()
val height = if(optHght == None) row.heigth.getOrElse() else optHght.getOrElse()
if (row.pic != None) { withoutSizeLimit {
encodeResponseWith(Gzip) {
complete(
HttpEntity(
ContentTypes.`application/octet-stream`,
ByteArrayToSource(Imaging.setImageSize(row.pic.get.getData, width, height)
))
)
}
}
} else complete(StatusCodes.NotFound)
case None => complete(StatusCodes.NotFound)
}
case Failure(err) => complete(err)
}
}
}
最后我们把这个Route组装在main route里:
implicit val webPicDao = new MongoRepo[WebPic]("testdb","pms", WebPic.fromDocument)
... pathPrefix("public") {
(pathPrefix("crud")) {
new MongoRoute[Person]("person")(personDao)
.route ~
new MongoRoute[Photo]("photo")(picDao)
.route
} ~
new MongoRoute[WebPic]("pms")(webPicDao).route
}
下面是本次示范的源代码:
MongoModel.scala
package com.datatech.rest.mongo
import org.mongodb.scala._
import com.datatech.sdp.mongo.engine._
import MGOClasses._ object MongoModels { case class Person(
userid: String = "",
name: String = "",
age: Option[Int] = None,
dob: Option[MGODate] = None,
address: Option[String] = None
) extends ModelBase[Document] {
import org.mongodb.scala.bson._ override def to: Document = {
var doc = Document(
"userid" -> this.userid,
"name" -> this.name) if (this.age != None)
doc = doc + ("age" -> this.age.get) if (this.dob != None)
doc = doc + ("dob" -> this.dob.get) if (this.address != None)
doc = doc + ("address" -> this.address.getOrElse("")) doc
} }
object Person {
val fromDocument: Document => Person = doc => {
val keyset = doc.keySet
Person(
userid = doc.getString("userid"),
name = doc.getString("name"),
age = mgoGetIntOrNone(doc,"age").asInstanceOf[Option[Int]], dob = {if (keyset.contains("dob"))
Some(doc.getDate("dob"))
else None }, address = mgoGetStringOrNone(doc,"address")
)
}
} case class Photo (
id: String,
loc: Option[String],
size: Option[Int],
credt: Option[MGODate],
photo: Option[MGOBlob]
) extends ModelBase[Document] {
override def to: Document = {
var doc = Document("id" -> this.id)
if (loc != None)
doc = doc + ("loc" -> this.loc.get)
if (size != None)
doc = doc + ("size" -> this.size.get)
if (credt != None)
doc = doc + ("credt" -> this.credt.get)
if (photo != None)
doc = doc + ("photo" -> this.photo.get)
doc
}
} object Photo {
def fromDocument: Document => Photo = doc => {
Photo(
id = doc.getString("id"),
loc = mgoGetStringOrNone(doc,"loc"),
size = mgoGetIntOrNone(doc,"size").asInstanceOf[Option[Int]],
credt = mgoGetDateOrNone(doc,"credt"),
photo = mgoGetBlobOrNone(doc, "photo")
)
}
} case class WebPic(
pid: String,
seqno: Int,
desc: Option[String],
width: Option[Int],
heigth: Option[Int],
pic: Option[MGOBlob]
) extends ModelBase[Document] { self =>
override def to: Document = {
var doc = Document(
"pid" -> self.pid,
"seqno" -> self.seqno
)
if (self.desc != None)
doc = doc + ("desc" -> self.desc.get)
if (self.width != None)
doc = doc + ("width" -> self.width.get)
if (self.heigth != None)
doc = doc + ("heigth" -> self.heigth.get)
if (self.pic != None)
doc = doc + ("photo" -> self.pic.get)
doc
}
}
object WebPic {
def fromDocument: Document => WebPic = doc => {
WebPic(
pid = doc.getString("pid"),
seqno = doc.getInteger("seqno"),
desc = mgoGetStringOrNone(doc,"desc"),
width = mgoGetIntOrNone(doc,"width").asInstanceOf[Option[Int]],
heigth = mgoGetIntOrNone(doc,"heigth").asInstanceOf[Option[Int]],
pic = None
)
}
}
}
MongoRepo.scala
package com.datatech.rest.mongo
import org.mongodb.scala._
import org.bson.conversions.Bson
import org.mongodb.scala.result._
import com.datatech.sdp.mongo.engine._
import MGOClasses._
import MGOEngine._
import MGOCommands._
import com.datatech.sdp.result.DBOResult.DBOResult object MongoRepo { class MongoRepo[R](db:String, coll: String, converter: Option[Document => R])(implicit client: MongoClient) {
def getAll(next:Option[String],sort:Option[String],fields:Option[String],top:Option[Int]): DBOResult[Seq[R]] = {
var res = Seq[ResultOptions]()
next.foreach {b => res = res :+ ResultOptions(FOD_TYPE.FOD_FILTER,Some(Document(b)))}
sort.foreach {b => res = res :+ ResultOptions(FOD_TYPE.FOD_SORT,Some(Document(b)))}
fields.foreach {b => res = res :+ ResultOptions(FOD_TYPE.FOD_PROJECTION,Some(Document(b)))}
top.foreach {b => res = res :+ ResultOptions(FOD_TYPE.FOD_LIMIT,None,b)} val ctxFind = MGOContext(dbName = db,collName=coll)
.setActionType(MGO_ACTION_TYPE.MGO_QUERY)
.setCommand(Find(andThen = res))
mgoQuery[Seq[R]](ctxFind,converter)
} def query(filtr: Bson, next:Option[String]=None,sort:Option[String]=None,fields:Option[String]=None,top:Option[Int]=None): DBOResult[Seq[R]] = {
var res = Seq[ResultOptions]()
next.foreach {b => res = res :+ ResultOptions(FOD_TYPE.FOD_FILTER,Some(Document(b)))}
sort.foreach {b => res = res :+ ResultOptions(FOD_TYPE.FOD_SORT,Some(Document(b)))}
fields.foreach {b => res = res :+ ResultOptions(FOD_TYPE.FOD_PROJECTION,Some(Document(b)))}
top.foreach {b => res = res :+ ResultOptions(FOD_TYPE.FOD_LIMIT,None,b)}
val ctxFind = MGOContext(dbName = db,collName=coll)
.setActionType(MGO_ACTION_TYPE.MGO_QUERY)
.setCommand(Find(filter = Some(filtr),andThen = res))
mgoQuery[Seq[R]](ctxFind,converter)
} import org.mongodb.scala.model.Filters._
def count(pid: String):DBOResult[Int] = {
val ctxCount = MGOContext(dbName = db,collName=coll)
.setActionType(MGO_ACTION_TYPE.MGO_QUERY)
.setCommand(Count(filter=Some(equal("pid",pid))))
mgoQuery[Int](ctxCount,None)
} def getOneDocument(filtr: Bson): DBOResult[Document] = {
val ctxFind = MGOContext(dbName = db,collName=coll)
.setActionType(MGO_ACTION_TYPE.MGO_QUERY)
.setCommand(Find(filter = Some(filtr),firstOnly = true))
mgoQuery[Document](ctxFind,None)
}
def getOnePicture(pid: String, seqno: Int): DBOResult[R] = {
val ctxFind = MGOContext(dbName = db, collName = coll)
.setActionType(MGO_ACTION_TYPE.MGO_QUERY)
.setCommand(Find(filter = Some(and(equal("pid",pid),equal("seqno",seqno))), firstOnly = true))
mgoQuery[R](ctxFind, converter)
}
def insert(doc: Document): DBOResult[Completed] = {
val ctxInsert = MGOContext(dbName = db,collName=coll)
.setActionType(MGO_ACTION_TYPE.MGO_UPDATE)
.setCommand(Insert(Seq(doc)))
mgoUpdate[Completed](ctxInsert)
} def delete(filter: Bson): DBOResult[DeleteResult] = {
val ctxDelete = MGOContext(dbName = db,collName=coll)
.setActionType(MGO_ACTION_TYPE.MGO_UPDATE)
.setCommand(Delete(filter))
mgoUpdate[DeleteResult](ctxDelete)
} def update(filter: Bson, update: Bson, many: Boolean): DBOResult[UpdateResult] = {
val ctxUpdate = MGOContext(dbName = db,collName=coll)
.setActionType(MGO_ACTION_TYPE.MGO_UPDATE)
.setCommand(Update(filter,update,None,!many))
mgoUpdate[UpdateResult](ctxUpdate)
} def replace(filter: Bson, row: Document): DBOResult[UpdateResult] = {
val ctxUpdate = MGOContext(dbName = db,collName=coll)
.setActionType(MGO_ACTION_TYPE.MGO_UPDATE)
.setCommand(Replace(filter,row))
mgoUpdate[UpdateResult](ctxUpdate)
} } }
MongoRoute.scala
package com.datatech.rest.mongo
import akka.http.scaladsl.server.Directives
import com.datatech.sdp.file._ import scala.util._
import org.mongodb.scala._
import com.datatech.sdp.file.Streaming._
import org.mongodb.scala.result._
import MongoRepo._
import akka.stream.ActorMaterializer
import com.datatech.sdp.result.DBOResult._
import org.mongodb.scala.model.Filters._
import com.datatech.sdp.mongo.engine.MGOClasses._
import monix.execution.CancelableFuture
import akka.util._
import akka.http.scaladsl.model._
import akka.http.scaladsl.coding.Gzip
import com.datatech.rest.mongo.MongoModels.WebPic import scala.concurrent._
import scala.concurrent.duration._
object MongoRoute {
class MongoRoute[M <: ModelBase[Document]](val pathName: String)(repository: MongoRepo[M])(
implicit c: MongoClient, m: Manifest[M], mat: ActorMaterializer) extends Directives with JsonConverter {
import monix.execution.Scheduler.Implicits.global
var dbor: DBOResult[Seq[M]] = _
var dbou: DBOResult[UpdateResult] = _
val route = pathPrefix(pathName) {
pathPrefix("pictures") {
(post & parameters('pid,'desc.?,'width.as[Int].?,'heigth.as[Int].?)) { (pid, optDesc, optWid, optHgh) =>
val futCount: Future[Int] = repository.count(pid).value.value.runToFuture.map {
eoi =>
eoi match {
case Right(oi) => oi match {
case Some(i) => i
case None => -
}
case Left(err) => -
}
}
val count: Int = Await.result(futCount, seconds)
var doc = Document(
"pid" -> pid,
"seqno" -> count
)
if (optDesc != None)
doc = doc + ("desc" -> optDesc.get)
if (optWid != None)
doc = doc + ("desc" -> optWid.get)
if (optHgh != None)
doc = doc + ("desc" -> optHgh.get) withoutSizeLimit {
decodeRequest {
extractDataBytes { bytes =>
val fut = bytes.runFold(ByteString()) { case (hd, bs) =>
hd ++ bs
}
onComplete(fut) {
case Success(b) =>
doc = doc + ("pic" -> b.toArray)
val futmsg: Future[String] = repository.insert(doc).value.value.runToFuture.map {
eoc =>
eoc match {
case Right(oc) => oc match {
case Some(c) => count.toString // c.toString()
case None => "insert may not complete!"
}
case Left(err) => err.getMessage
}
}
complete(futmsg)
case Failure(err) => complete(err)
}
}
}
}
} ~
(get & parameters('pid, 'seqno.as[Int].?,'width.as[Int].?,'height.as[Int].?)) {
(pid, optSeq, optWid,optHght) =>
if (optSeq == None) {
dbor = repository.query(equal("pid", pid))
val futRows = dbor.value.value.runToFuture.map {
eolr =>
eolr match {
case Right(olr) => olr match {
case Some(lr) => lr
case None => Seq[M]()
}
case Left(_) => Seq[M]()
}
}
complete(futureToJson(futRows))
} else {
val futOptPicRow: CancelableFuture[Option[WebPic]] = repository.getOnePicture(pid,optSeq.get)
.value.value.runToFuture.map {
eorow =>
eorow match {
case Right(orow) => orow match {
case Some(row) =>
if (row == null) None
else Some(row.asInstanceOf[WebPic])
case None => None
}
case Left(_) => None
}
}
onComplete(futOptPicRow) {
case Success(optRow) => optRow match {
case Some(row) =>
val width = if(optWid == None) row.width.getOrElse() else optWid.getOrElse()
val height = if(optHght == None) row.heigth.getOrElse() else optHght.getOrElse()
if (row.pic != None) { withoutSizeLimit {
encodeResponseWith(Gzip) {
complete(
HttpEntity(
ContentTypes.`application/octet-stream`,
ByteArrayToSource(Imaging.setImageSize(row.pic.get.getData, width, height)
))
)
}
}
} else complete(StatusCodes.NotFound)
case None => complete(StatusCodes.NotFound)
}
case Failure(err) => complete(err)
}
}
}
} ~
pathPrefix("blob") {
(get & parameter('filter)) { filter =>
val filtr = Document(filter)
val futOptPic: CancelableFuture[Option[MGOBlob]] = repository.getOneDocument(filtr).value.value.runToFuture.map {
eodoc =>
eodoc match {
case Right(odoc) => odoc match {
case Some(doc) =>
if (doc == null) None
else mgoGetBlobOrNone(doc, "photo")
case None => None
}
case Left(_) => None
}
}
onComplete(futOptPic) {
case Success(optBlob) => optBlob match {
case Some(blob) =>
withoutSizeLimit {
encodeResponseWith(Gzip) {
complete(
HttpEntity(
ContentTypes.`application/octet-stream`,
ByteArrayToSource(blob.getData)
)
)
}
}
case None => complete(StatusCodes.NotFound)
}
case Failure(err) => complete(err)
}
} ~
(post & parameter('bson)) { bson =>
val bdoc = Document(bson)
withoutSizeLimit {
decodeRequest {
extractDataBytes { bytes =>
val fut = bytes.runFold(ByteString()) { case (hd, bs) =>
hd ++ bs
}
onComplete(fut) {
case Success(b) =>
val doc = bdoc + ("photo" -> b.toArray)
val futmsg = repository.insert(doc).value.value.runToFuture.map {
eoc =>
eoc match {
case Right(oc) => oc match {
case Some(c) => c.toString()
case None => "insert may not complete!"
}
case Left(err) => err.getMessage
}
}
complete(futmsg)
case Failure(err) => complete(err)
}
}
}
}
}
} ~
(get & parameters('filter.?,'fields.?,'sort.?,'top.as[Int].?,'next.?)) {
(filter,fields,sort,top,next) => {
dbor = {
filter match {
case Some(fltr) => repository.query(Document(fltr),next,sort,fields,top)
case None => repository.getAll(next,sort,fields,top)
}
}
val futRows = dbor.value.value.runToFuture.map {
eolr =>
eolr match {
case Right(olr) => olr match {
case Some(lr) => lr
case None => Seq[M]()
}
case Left(_) => Seq[M]()
}
}
complete(futureToJson(futRows))
}
} ~ post {
entity(as[String]) { json =>
val extractedEntity: M = fromJson[M](json)
val doc: Document = extractedEntity.to
val futmsg = repository.insert(doc).value.value.runToFuture.map {
eoc =>
eoc match {
case Right(oc) => oc match {
case Some(c) => c.toString()
case None => "insert may not complete!"
}
case Left(err) => err.getMessage
}
} complete(futmsg)
}
} ~ (put & parameter('filter,'set.?, 'many.as[Boolean].?)) { (filter, set, many) =>
val bson = Document(filter)
if (set == None) {
entity(as[String]) { json =>
val extractedEntity: M = fromJson[M](json)
val doc: Document = extractedEntity.to
val futmsg = repository.replace(bson, doc).value.value.runToFuture.map {
eoc =>
eoc match {
case Right(oc) => oc match {
case Some(d) => s"${d.getMatchedCount} matched rows, ${d.getModifiedCount} rows updated."
case None => "update may not complete!"
}
case Left(err) => err.getMessage
}
}
complete(futureToJson(futmsg))
}
} else {
set match {
case Some(u) =>
val ubson = Document(u)
dbou = repository.update(bson, ubson, many.getOrElse(true))
case None =>
dbou = Left(new IllegalArgumentException("missing set statement for update!"))
}
val futmsg = dbou.value.value.runToFuture.map {
eoc =>
eoc match {
case Right(oc) => oc match {
case Some(d) => s"${d.getMatchedCount} matched rows, ${d.getModifiedCount} rows updated."
case None => "update may not complete!"
}
case Left(err) => err.getMessage
}
}
complete(futureToJson(futmsg))
}
} ~ (delete & parameters('filter, 'many.as[Boolean].?)) { (filter,many) =>
val bson = Document(filter)
val futmsg = repository.delete(bson).value.value.runToFuture.map {
eoc =>
eoc match {
case Right(oc) => oc match {
case Some(d) => s"${d.getDeletedCount} rows deleted."
case None => "delete may not complete!"
}
case Left(err) => err.getMessage
}
}
complete(futureToJson(futmsg))
}
}
} }
PMSServer.scala
package com.datatech.rest.mongo import akka.actor._
import akka.stream._
import akka.http.scaladsl.Http
import akka.http.scaladsl.server.Directives._
import pdi.jwt._
import AuthBase._
import MockUserAuthService._
import org.mongodb.scala._ import scala.collection.JavaConverters._
import MongoModels._
import MongoRepo._
import MongoRoute._ object PMSServer extends App { implicit val httpSys = ActorSystem("httpSystem")
implicit val httpMat = ActorMaterializer()
implicit val httpEC = httpSys.dispatcher val settings: MongoClientSettings = MongoClientSettings.builder()
.applyToClusterSettings(b => b.hosts(List(new ServerAddress("localhost")).asJava))
.build()
implicit val client: MongoClient = MongoClient(settings)
implicit val personDao = new MongoRepo[Person]("testdb","person", Some(Person.fromDocument))
implicit val picDao = new MongoRepo[Photo]("testdb","photo", None)
implicit val webPicDao = new MongoRepo[WebPic]("testdb","pms", WebPic.fromDocument)
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("private") {
authenticateOAuth2(realm = "private", authenticator.authenticateToken) { validToken =>
FileRoute(validToken)
.route
// ~ ...
}
} ~
pathPrefix("public") {
(pathPrefix("crud")) {
new MongoRoute[Person]("person")(personDao)
.route ~
new MongoRoute[Photo]("photo")(picDao)
.route
} ~
new MongoRoute[WebPic]("pms")(webPicDao).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()) }
imaging.scala
package com.datatech.sdp.file
import javax.imageio.ImageIO
import java.awt.Graphics2D
import java.awt.image.BufferedImage
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream object Imaging {
def setImageSize(barr: Array[Byte], wth: Int, hth: Int): Array[Byte] = {
val input = ImageIO.read(new ByteArrayInputStream(barr))
val image = new BufferedImage(wth, hth, BufferedImage.TYPE_INT_BGR)
val g = image.getGraphics.asInstanceOf[Graphics2D]
g.drawImage(input, , , wth, hth, null) //画图
g.dispose()
image.flush()
val barros = new ByteArrayOutputStream()
ImageIO.write(image, "jpg", barros)
barr
}
}
restapi(5)- rest-mongo 应用实例:分布式图片管理系统之一,rest 服务的更多相关文章
- WCF分布式开发步步为赢(9):WCF服务实例激活类型编程与开发
.Net Remoting的激活方式也有三种:SingleTon模式.SingleCall模式.客户端激活方式,WCF服务实例激活类型包括三种方式:单调服务(Call Service),会话服务(Se ...
- 每天一个JavaScript实例-推断图片是否载入完毕
<!doctype html> <html lang="en"> <head> <meta charset="utf-8&quo ...
- 使用CSS3的clip-path(裁剪路径)实现剪贴区域的显示以及实例实现图片渐变
clip-path介绍 clip-path 直译过来就是裁剪路径,使用SVG或形状定义一个HTML元素的可见区域的方法.想象一下你在Photoshop中勾勒路径的场景.MDN上是这样介绍 clip-p ...
- FastDFS 与 Nginx 实现分布式图片服务器
FastDFS 与 Nginx 实现分布式图片服务器 本人的 Ubuntu18.04 用户名为 jj 点我下载所有所需的压缩包文件 一.FastDFS安装 1.安装 fastdfs 依赖包 ① 解压 ...
- CSS实例:图片导航块
1.认识CSS的 盒子模型. 2.CSS选择器的灵活使用. 3.实例: a.图片文字用div等元素布局形成HTML文件. b.新建相应CSS文件,并link到html文件中. c.CSS文件中定义样式 ...
- [C13] 应用实例:图片文字识别(Application Example: Photo OCR)
应用实例:图片文字识别(Application Example: Photo OCR) 问题描述和流程图(Problem Description and Pipeline) 图像文字识别应用所作的事是 ...
- 手把手教你用 FastDFS 构建分布式文件管理系统
说起分布式文件管理系统,大家可能很容易想到 HDFS.GFS 等系统,前者是 Hadoop 的一部分,后者则是 Google 提供的分布式文件管理系统.除了这些之外,国内淘宝和腾讯也有自己的分布式文件 ...
- WCF分布式开发步步为赢(3)WCF服务元数据交换、配置及编程开发
今天我们继续WCF分布式开发步步为赢(3)WCF服务元数据交换.配置及编程开发的学习.经过前面两节的学习,我们了解WCF分布式开发的相关的基本的概念和自定义宿主托管服务的完整的开发和配置过程.今天我们 ...
- (五):C++分布式实时应用框架——微服务架构的演进
C++分布式实时应用框架--微服务架构的演进 上一篇:(四):C++分布式实时应用框架--状态中心模块 版权声明:本文版权及所用技术归属smartguys团队所有,对于抄袭,非经同意转载等行为保留法律 ...
随机推荐
- (CVE-2017-10271)weblogic12.1.3.0漏洞测试与打补丁过程
1.漏洞测试 搭建完成weblogic12.1.3.0后,开始用工具测试 点击connect,右下角显示connected,说明已连接→说明漏洞存在 CMD输入:ls ,然后点击Execute执行 ...
- 个人永久性免费-Excel催化剂功能第96波-地图数据挖宝之全国天气查询(区域最细可到区县,最长预报4天)
天气预报的信息,是很普通的大家习以为常的信息,但如果不进行采集,在日常数据分析过程中,就少了非常重要的一个分析维度,如果人手采集整理,工作量巨大.此篇给广大数据分析工作者再次减负,只需简单一键,即可批 ...
- 个人永久性免费-Excel催化剂功能第33波-报表形式数据结构转标准数据源
一般来说,如果有标准的数据源结构,对后续的分析工作将会带来极大的方便.但现实中,许多的原始数据并不预期那样,一个主题的数据已经干净地存放在一个工作表中.今天Excel催化剂再次送上批量化操作,将不规则 ...
- Linux卸载MySql——ubuntu版
卸载mysql 1)删除mysql的数据文件 sudo rm /var/lib/mysql/ -R 2)删除mqsql的配置文件 sudo rm /etc/mysql/ -R 3)自动卸载mysql的 ...
- Android入门简介
GeoQuiz应用是由一个activity和一个布局(layout)组成. activity是Android SDK中Activity类的一个具体实例,负责管理用户与信息屏的交互. 布局定义了一系列用 ...
- 访问腾讯云主机mysql的设置问题
---恢复内容开始--- 1.需要打开腾讯云主机的防火墙端口3306,具体实现可以在腾讯云控制台安全组中设置开放全部端口(安全组对应入站.出战规则): 2.将云主机mysql中mysql数据库user ...
- Linux常用命令3
useradd 添加用户账号 -n 制定uid标记号 -d 指定宿主目录,缺省默认为/home/用户名 -e 制定账号失效时间 -M 不为用户建立初始化宿主目录(通常作为不登陆账号) -s 指定用户的 ...
- Thinkphp 3.2.3 parseWhere设计缺陷导致update/delete注入 分析
目录 分析 总结 分析 首先看一下控制器,功能是根据用户传来的id,修改对应用户的密码. 13行把用户传来的id参数送入where()作为SQL语句中的WHERE语句,将pwd参数送入save()作为 ...
- Charles(Windows/Android)入门使用
一. 介绍以及下载(windows) Charles是一个HTTP代理/HTTP监视器/反向代理,使开发人员能够查看其机器和Internet之间所有HTTP和SSL/HTTPS流量,这包括请求,响应和 ...
- 【iOS】设置 rootViewController
iOS 开发中,rootViewController 经常用到,示例代码如下: self.window = [[UIWindow alloc] initWithFrame:[UIScreen main ...