目的:在IDEA中实现图片、日期等相关的类型在mongodb存储读取
- 主要是Scala和mongodb里面的类型的转换。Scala里面的数据编码类型和mongodb里面的存储的数据类型各个不同。存在类型转换。
- 而图片和日期的转换如下图所示。
1、日期的存取
- 简单借助java.until.Calendar即可。
val ca=Calendar.getInstance() ca.set() ca.getTime
- 有多种具体的格式等,再直接应用mgoDateTime等方法
//显示各种格式 type MGODate = java.util.Date def mgoDate(yyyy: Int, mm: Int, dd: Int): MGODate = { val ca = Calendar.getInstance() ca.set(yyyy,mm,dd) ca.getTime() } def mgoDateTime(yyyy: Int, mm: Int, dd: Int, hr: Int, min: Int, sec: Int): MGODate = { val ca = Calendar.getInstance() ca.set(yyyy,mm,dd,hr,min,sec) ca.getTime() } def mgoDateTimeNow: MGODate = { val ca = Calendar.getInstance() ca.getTime } def mgoDateToString(dt: MGODate, formatString: String): String = { val fmt= new SimpleDateFormat(formatString) fmt.format(dt) }
2、图片的存取(看图片大小,一般都是如下,大于16M的图片即采用GridFS,分别将图片的属性存储)
借助Akka的FileStream
将File(图片)===》Array[Byte]代码格式,图片在mongodb中显示形式binary
具体代码如下:
FileStreaming.scala
package com.company.files import java.nio.file.Paths import java.nio._ import java.io._ import akka.stream.{Materializer} import akka.stream.scaladsl.{FileIO, StreamConverters} import scala.concurrent.{Await} import akka.util._ import scala.concurrent.duration._ object FileStreaming { def FileToByteBuffer(fileName: String, timeOut: FiniteDuration)( implicit mat: Materializer):ByteBuffer = { val fut = FileIO.fromPath(Paths.get(fileName)).runFold(ByteString()) { case (hd, bs) => hd ++ bs } (Await.result(fut, timeOut)).toByteBuffer } def FileToByteArray(fileName: String, timeOut: FiniteDuration)( implicit mat: Materializer): Array[Byte] = { val fut = FileIO.fromPath(Paths.get(fileName)).runFold(ByteString()) { case (hd, bs) => hd ++ bs } (Await.result(fut, timeOut)).toArray } def FileToInputStream(fileName: String, timeOut: FiniteDuration)( implicit mat: Materializer): InputStream = { val fut = FileIO.fromPath(Paths.get(fileName)).runFold(ByteString()) { case (hd, bs) => hd ++ bs } val buf = (Await.result(fut, timeOut)).toArray new ByteArrayInputStream(buf) } def ByteBufferToFile(byteBuf: ByteBuffer, fileName: String)( implicit mat: Materializer) = { val ba = new Array[Byte](byteBuf.remaining()) byteBuf.get(ba,0,ba.length) val baInput = new ByteArrayInputStream(ba) val source = StreamConverters.fromInputStream(() => baInput) //ByteBufferInputStream(bytes)) source.runWith(FileIO.toPath(Paths.get(fileName))) } def ByteArrayToFile(bytes: Array[Byte], fileName: String)( implicit mat: Materializer) = { val bb = ByteBuffer.wrap(bytes) val baInput = new ByteArrayInputStream(bytes) val source = StreamConverters.fromInputStream(() => baInput) //ByteBufferInputStream(bytes)) source.runWith(FileIO.toPath(Paths.get(fileName))) } def InputStreamToFile(is: InputStream, fileName: String)( implicit mat: Materializer) = { val source = StreamConverters.fromInputStream(() => is) source.runWith(FileIO.toPath(Paths.get(fileName))) } }
- Helpers.scala
package com.company.lib import java.util.concurrent.TimeUnit import scala.concurrent.Await import scala.concurrent.duration.Duration import java.text.SimpleDateFormat import java.util.Calendar import org.mongodb.scala._ object Helpers { implicit class DocumentObservable[C](val observable: Observable[Document]) extends ImplicitObservable[Document] { override val converter: (Document) => String = (doc) => doc.toJson } implicit class GenericObservable[C](val observable: Observable[C]) extends ImplicitObservable[C] { override val converter: (C) => String = (doc) => doc.toString } trait ImplicitObservable[C] { val observable: Observable[C] val converter: (C) => String def results(): Seq[C] = Await.result(observable.toFuture(), Duration(10, TimeUnit.SECONDS)) def headResult() = Await.result(observable.head(), Duration(10, TimeUnit.SECONDS)) def printResults(initial: String = ""): Unit = { if (initial.length > 0) print(initial) results().foreach(res => println(converter(res))) } def printHeadResult(initial: String = ""): Unit = println(s"${initial}${converter(headResult())}") } type MGODate = java.util.Date def mgoDate(yyyy: Int, mm: Int, dd: Int): MGODate = { val ca = Calendar.getInstance() ca.set(yyyy,mm,dd) ca.getTime() } def mgoDateTime(yyyy: Int, mm: Int, dd: Int, hr: Int, min: Int, sec: Int): MGODate = { val ca = Calendar.getInstance() ca.set(yyyy,mm,dd,hr,min,sec) ca.getTime() } def mgoDateTimeNow: MGODate = { val ca = Calendar.getInstance() ca.getTime } def mgoDateToString(dt: MGODate, formatString: String): String = { val fmt= new SimpleDateFormat(formatString) fmt.format(dt) } }
- Model.scala
模型中包含了两个具体的模型Person和Address,二者是包含关系,具体模型内含存取方法
package com.company.demo import org.mongodb.scala._ import bson._ import java.util.Calendar import com.company.files._ import akka.stream.ActorMaterializer object Models { //Model可存在在不同的模型,下面存在一个拥有代表性的模型Person case class Address( //Scala中的字段类型,且String的默认参数是“” city: String ="", zipcode: String = "" ) { def toDocument: Document = bson.Document( //bson.Document是bson包里面的Document,其他包内有不同的Document "city" -> city, "zipcode" -> zipcode ) def fromDocument(doc: Document): Address = this.copy( city = doc.getString("city"), zipcode = doc.getString("zipcode") ) } //这是日期的设置 val ca = Calendar.getInstance() ca.set(2001,10,23) val defaultDate = ca.getTime case class Person ( lastName: String = "Doe", firstName: String = "John", age: Int = 1, phones: List[String] = Nil, address: List[Address] = Nil, birthDate: java.util.Date = defaultDate, picture: Array[Byte] = Array() ) { ctx => def toDocument: Document = { var doc = bson.Document( "lastName" -> ctx.lastName, "firstName" -> ctx.firstName, "age" -> ctx.age, "birthDate" -> ctx.birthDate ) if (ctx.phones != Nil) doc = doc + ("phones" -> ctx.phones) if (ctx.address != Nil) doc = doc + ("address" -> ctx.address.map(addr => addr.toDocument)) if (!ctx.picture.isEmpty) doc = doc + ("picture" -> ctx.picture) doc } import scala.collection.JavaConverters._ def fromDocument(doc: Document): Person = { //keySet val ks = doc.keySet ctx.copy( lastName = doc.getString("lastName"), firstName = doc.getString("firstName"), age = doc.getInteger("age"), phones = { doc.get("phones").asInstanceOf[Option[BsonArray]] match { case Some(barr) => barr.getValues.asScala.toList.map(_.toString) case None => Nil } }, address = { if (ks.contains("address")) { doc.get("address").asInstanceOf[Option[BsonArray]] match { case Some(barr) => barr.getValues.asScala.toList.map ( ad => Address().fromDocument(ad.asDocument()) ) case None => Nil } } else Nil }, picture = { if (ks.contains("picture")) { doc.get("picture").asInstanceOf[Option[BsonBinary]] match { case Some(ba) => ba.getData case None => Array() } } else Array() } ) } //在控制台显示的格式。 def toSink()(implicit mat: ActorMaterializer) = { println(s"LastName: ${ctx.lastName}") println(s"firstName: ${ctx.firstName}") println(s"age: ${ctx.age}") println(s"phones: ${ctx.phones}") println(s"address ${ctx.address}") if(!ctx.picture.isEmpty) { val path = s"/img/${ctx.firstName}.jpg" FileStreaming.ByteArrayToFile(ctx.picture,path) println(s"picture saved to: ${path}") } } } }
- PersonCRUD.scala简单测试
package com.company.demo import org.mongodb.scala._ import bson._ import java.util.Calendar import scala.collection.JavaConverters._ import com.company.lib.Helpers._ import com.company.files.FileStreaming._ import akka.actor._ import akka.stream._ import scala.concurrent.duration._ import scala.util._ object PersonCRUD extends App { import Models._ implicit val system = ActorSystem() implicit val ec = system.dispatcher implicit val mat = ActorMaterializer() // or provide custom MongoClientSettings val settings: MongoClientSettings = MongoClientSettings.builder() .applyToClusterSettings(b => b.hosts(List(new ServerAddress("localhost")).asJava)) .build() val client: MongoClient = MongoClient(settings) val mydb = client.getDatabase("mydb") val mytable = mydb.getCollection("personal") val susan = Person( lastName = "Wang", firstName = "Susan", age = 18, phones = List("137110998","189343661"), address = List( Address("Sz","101992"), Address(city = "gz", zipcode="231445") ), birthDate = mgoDate(2001,5,8), picture = FileToByteArray("/img/sc.jpg",3 seconds) ) /* val futResult = mytable.insertOne(susan.toDocument).toFuture() futResult.onComplete { case Success(value) => println(s"OK! ${value}") case Failure(err) => println(s"Boom!!! ${err.getMessage}") } scala.io.StdIn.readLine() */
mytable.find().toFuture().andThen { case Success(ps) => ps.foreach(Person().fromDocument(_).toSink()) case Failure(err) => println(s"ERROR: ${err.getMessage}") } scala.io.StdIn.readLine() system.terminate() }