从MongoDB调整图像大小的反应方式

时间:2014-02-11 07:21:34

标签: mongodb scala asynchronous gridfs reactivemongo

我正在寻找一种反应方式来调整存储在GridFS中的图像。

有一篇文章here,但遗憾的是它使用的Casbah不是非笨蛋。

还有good library用于调整图像大小。虽然它支持异步操作,但我找不到一种方法来重新调整图像块的大小。也许根本不可能。然后我很好,但是请你帮我理解如何将Enumerator(我从GridFS获得)转换为scrimage(image resizer lib)可用的简单流。

2 个答案:

答案 0 :(得分:3)

我必须首先开始(我发现我一直这样做)是首先考虑你为什么要使用GridFS。

正如我之前所说,这来自于一个常见的误解,即GridFS是MongoDB存储文件的方式,因此这就是你所使用的。所以我建议任何人考虑,甚至阅读这篇文章,以便阅读以下两个链接上的文件:

http://docs.mongodb.org/manual/faq/developers/#when-should-i-use-gridfs

http://docs.mongodb.org/manual/core/gridfs/

总结是,GridFS的唯一目的是能够存储更大的内容 16MB BSON文档限制。实质上,文档将 chunked 分成更小(超过16MB)的部分并插入到特殊集合中。这有助于使用简化的界面处理读取和写入的大小限制,以获取所需的所有文档。

额外信息是GridFS MongoDB魔法。 服务器不知道存储的信息,只是它只是另一个文档。因此,GridFS是驱动程序规范实现,这意味着每次读取和写入都会通过网络产生多个文档请求

现在真正的问题是,当您的内容低于16MB 时,您最好只将其作为数据插入普通文档字段(二进制文件当然需要进行base64编码),并且你所有的读写都是通过电线以一次性进行的。

对于这个实现案例,你的图像小于16MB,你会得到一个单独的文档,带有一个包含字符串的字段,这对于解析流来说很简单(来自base64)和返回内容。或者基本上是转换上的任何内容,因为您不必担心返回到MongoDB服务器的其他调用以获得更多“块”。

如果您真的需要200MB High Res Photoshop文档作为您的数据源,那么请继续使用GridFS。在这种情况下,这可能就是你想要的。

没有装袋GridFS,这是一个非常好的主意。只是大多数使用它的人并没有将它用于它的设计目标。

P.S进行任何流式转换的真正问题是,为了做到这一点,你需要几个关于图像的元数据(通常不能从库方法获得,直到可以访问整个文件内容)。因此,无论如何,您都要存储自定义信息,以实现目标。

答案 1 :(得分:0)

根据我的理解,存储和从GridFS中获取二进制数据总是有意义的,因为MongoDB / ReactiveMongo使用整个文档的“流”来回答查询。将(潜在的巨大的)二进制数据存储在一个文档中不允许真正流式传输数据。相反,二进制数据首先被完全读入存储器,由于资源限制,这不是一个好主意。

回到原来的问题,这里的主要问题基本上是如何将OutputStream(Iteratee可能写入的)连接到图像处理库所需的InputStream。有关详细信息,请参阅http://ostermiller.org/convert_java_outputstream_inputstream.html

以下代码应该有希望解释这个概念:

import java.io._
import java.util.Arrays
import scala.concurrent._
import ExecutionContext.Implicits.global
import play.api.libs.iteratee._

object StreamingDemo extends App {

  // this enumerator will come from GridFS in your scenario
  val enumerator = Enumerator[Array[Byte]](Array(1, 2), Array(3), Array(4, 5, 6)) andThen Enumerator.eof

  val in = new PipedInputStream();
  val out = new PipedOutputStream(in);

  def putDataOnOutputStream(out: OutputStream) = {
    // as we have a Future here writing to the OutputStream is done in a separate thread as needed with piping
    enumerator.apply(Iteratee.foreach { elem =>
      println("write...");
      out.write(elem)
      out.flush()
    }).onComplete { _ =>
      out.close()
    }
  }

  def processDataFromInputStream(in: InputStream) = {
    var res: Int = in.read
    while (res != -1) {
      println("read: " + res);
      res = in.read
    }
    in.close()
  }

  putDataOnOutputStream(out)
  processDataFromInputStream(in);

}