我正在寻找一种反应方式来调整存储在 GridFS 中的图像大小。
这里有一篇文章,但不幸的是它使用了非阻塞的 Casbah。
还有一个很好的图像调整库。尽管它支持异步操作,但我找不到逐块调整图像大小的方法。也许根本不可能。那么我很好,但请您帮助我了解如何将 Enumerator(我从 GridFS 获得)转换为可由 scrimage(图像调整器库)使用的简单流。
我必须首先做的事情,(我发现我总是这样做)是让您考虑为什么首先使用 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,这是一个非常好的主意。只是大多数使用它的人并没有将它用于它的设计目的。
PS您进行任何流式转换的真正问题是您需要几条关于图像的元数据(通常在可以访问整个文件内容之前从库方法中不可用)才能做到这一点。因此,无论如何您都将存储自定义信息以实现您的目标。
在我的理解中,将二进制数据存储到 GridFS 或从 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);
}