最近有同事提起想把网页上的图片存在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 服务的更多相关文章

  1. WCF分布式开发步步为赢(9):WCF服务实例激活类型编程与开发

    .Net Remoting的激活方式也有三种:SingleTon模式.SingleCall模式.客户端激活方式,WCF服务实例激活类型包括三种方式:单调服务(Call Service),会话服务(Se ...

  2. 每天一个JavaScript实例-推断图片是否载入完毕

    <!doctype html> <html lang="en"> <head> <meta charset="utf-8&quo ...

  3. 使用CSS3的clip-path(裁剪路径)实现剪贴区域的显示以及实例实现图片渐变

    clip-path介绍 clip-path 直译过来就是裁剪路径,使用SVG或形状定义一个HTML元素的可见区域的方法.想象一下你在Photoshop中勾勒路径的场景.MDN上是这样介绍 clip-p ...

  4. FastDFS 与 Nginx 实现分布式图片服务器

    FastDFS 与 Nginx 实现分布式图片服务器 本人的 Ubuntu18.04 用户名为 jj 点我下载所有所需的压缩包文件 一.FastDFS安装 1.安装 fastdfs 依赖包 ① 解压 ...

  5. CSS实例:图片导航块

    1.认识CSS的 盒子模型. 2.CSS选择器的灵活使用. 3.实例: a.图片文字用div等元素布局形成HTML文件. b.新建相应CSS文件,并link到html文件中. c.CSS文件中定义样式 ...

  6. [C13] 应用实例:图片文字识别(Application Example: Photo OCR)

    应用实例:图片文字识别(Application Example: Photo OCR) 问题描述和流程图(Problem Description and Pipeline) 图像文字识别应用所作的事是 ...

  7. 手把手教你用 FastDFS 构建分布式文件管理系统

    说起分布式文件管理系统,大家可能很容易想到 HDFS.GFS 等系统,前者是 Hadoop 的一部分,后者则是 Google 提供的分布式文件管理系统.除了这些之外,国内淘宝和腾讯也有自己的分布式文件 ...

  8. WCF分布式开发步步为赢(3)WCF服务元数据交换、配置及编程开发

    今天我们继续WCF分布式开发步步为赢(3)WCF服务元数据交换.配置及编程开发的学习.经过前面两节的学习,我们了解WCF分布式开发的相关的基本的概念和自定义宿主托管服务的完整的开发和配置过程.今天我们 ...

  9. (五):C++分布式实时应用框架——微服务架构的演进

    C++分布式实时应用框架--微服务架构的演进 上一篇:(四):C++分布式实时应用框架--状态中心模块 版权声明:本文版权及所用技术归属smartguys团队所有,对于抄袭,非经同意转载等行为保留法律 ...

随机推荐

  1. [原创]OpenvSwitch安装

    一.安装环境: ubuntu-12.04-64bit 二.使用root权限,安装所需软件: apt-get install build-essential apt-get install openss ...

  2. Learning the Depths of Moving People by Watching Frozen

    基于双目的传统算法 对静止的物体, 在不同的 viewpoints 同一时刻进行拍摄, 根据拍摄到的结果, 使用三角测量算法计算出平面 2D 图像在 3D 图像中的坐标 单目 Ground Truth ...

  3. 15号作品teamfinal使用体验

    通过使用这款软件,可以轻松的查阅所处学期的任意周中某一天中的基教.一教.二教.三教和土木楼中的空教室,方便了同学去寻找空教室的方便,方便同学们上自习,节省寻找教室的时间,提供了非常大的便利. 打开界面 ...

  4. spark 源码分析之十七 -- Spark磁盘存储剖析

    上篇文章 spark 源码分析之十六 -- Spark内存存储剖析 主要剖析了Spark 的内存存储.本篇文章主要剖析磁盘存储. 总述 磁盘存储相对比较简单,相关的类关系图如下: 我们先从依赖类 Di ...

  5. github访问不到,登陆不上

    为github添加host C:\WINDOWS\System32\drivers\etc 在host文件添加如下两行 192.30.253.112 github.com 151.101.113.19 ...

  6. [leetcode] 621. Task Scheduler(medium)

    原题 思路: 按频率最大的字母来分块,频率最大的字母个数-1为分成的块数,每一块个数为n+1 比如AAABBCE,n=2, 则分为A-A- +A AAABBBCCEE,n=2,则分为AB-AB- +A ...

  7. 安科 OJ 1190 连接电脑 (并查集)

    时间限制:1 s 空间限制:128 M 传送门:https://oj.ahstu.cc/JudgeOnline/problem.php?id=1190 题目描述 机房里有若干台电脑,其中有一些电脑已经 ...

  8. Linnux命令大全(vim)

    vim复制和粘贴的基本命令(注:需先退出编辑模式)    yy复制游标所在行整行.或大写一个Y. (常用)    2yy或y2y复制两行. (常用)    y^复制至行首,或y0.不含游标所在处字元. ...

  9. 浅谈CMDB

    CMDB和运维自动化 一.运维 运维,指的是对已经搭建好的网络,软件,硬件进行维护.运维领域也是细分的,有硬件运维和软件运维 硬件运维主要包括对基础设施的运维,比如机房的设备,主机的硬盘,内存这些物理 ...

  10. linux初学者-pxe装机篇

    linux初学者-pxe装机篇 PXE的网络装机是客户机从自己的网卡启动,向本网络中的DHCP服务器索取ip,并从本网络的TFTP服务器中索取启动文件进行装机.此装机需要kickstart.tftp. ...