【问题标题】:Reactive way of resizing images from MongoDB从 MongoDB 调整图像大小的反应方式
【发布时间】:2014-03-08 20:50:30
【问题描述】:

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

有一篇文章here 但不幸的是它使用了不是非阻塞的Casbah。

还有一个good library 用于调整图像大小。尽管它支持异步操作,但我找不到逐块调整图像大小的方法。也许根本不可能。那么我很好,但请您帮助我了解如何将 Enumerator(我从 GridFS 获得)转换为可由 scrimage(图像调整器库)使用的简单流。

【问题讨论】:

    标签: mongodb scala asynchronous gridfs reactivemongo


    【解决方案1】:

    首先我必须开始(而且我发现我总是这样做)是让您考虑一下为什么首先使用 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文档限制。本质上,文档被分块 分成更小的(小于 16MB)部分并插入到一个特殊的集合中。这有助于通过简化的界面处理读取和写入的大小限制,以获取所有所需的文档。

    额外的信息是 GridFS 不是 MongoDB 魔法。 服务器 对存储的信息一无所知,除了它只是另一个文档。所以 GridFS 是一个驱动程序规范实现,这意味着每次读取和写入都会导致多个文档请求通过网络传输。

    现在真正的重点是,当您的内容小于 16MB 时,最好将其作为数据插入到普通文档字段中(二进制文件当然需要进行 base64 编码),并且您的所有读取和写入都通过网络一次性发生。

    对于这个实现案例,如果您的 图像 小于 16MB,您将返回一个文档,其中包含一个包含字符串的字段,这将很容易进行流解析(来自 base64)和返回内容。或者基本上任何关于转换的事情,因为您不必担心额外的调用会返回到 MongoDB 服务器以获得更多“块”。

    如果您真的需要 200MB 高分辨率 Photoshop 文档作为数据源,那么请务必使用 GridFS。在这种情况下,这可能就是您想要的。

    不装袋 GridFS,这是一个非常好的主意。只是大多数使用它的人并没有按照它的设计用途使用它。

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

    【讨论】:

    • 嗯。希望其他人投票。然后我将有权创建一个 GridFS 标记。
    • 实际上我已经开发了一个文件管理器,它允许用户管理文件,无论文件类型和大小。所以也许我必须存储超过 16MB 的文件。对我来说,使用相同的基础架构在 GridFS 中存储小文件和大文件更容易,因为它没有太多的 IMO 开销。所以最后一个问题是好的,我怎样才能从Enumerator 获得一个简单的流。
    • 或者最好在另一个问题中被问到!?
    • @A.Karimi 可能是这样,就其他问题而言。但是您不应该考虑也许的基础设施,因为这总是会导致糟糕的决策。一定的灵活性很好,但将实现保持在用例中。我的回复很大程度上是因为您在帖子中提到“图像”。所以我们说的是Web,我们说的是不到16MB。 (谁会等呢?)。这是我多次重复的非常有效的评论。您为此需要 GridFS,性能问题会杀死您。
    • 对,但我确信我们会有一些大于 16M 的文件,但它们绝对不是图像(文档、PDF 等)。所以你的意思是我应该有一个图像文件集合并为其他人使用 GridFS?
    【解决方案2】:

    据我了解,在 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);
    
    }
    

    【讨论】:

      猜你喜欢
      • 2018-04-22
      • 2021-08-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-02
      • 2018-01-10
      相关资源
      最近更新 更多