This repository has been archived by the owner on Jan 21, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 129
/
GridFS.scala
349 lines (288 loc) · 11.6 KB
/
GridFS.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
/**
* Copyright (c) 2010 - 2012 10gen, Inc. <http://10gen.com>
* Copyright (c) 2009, 2010 Novus Partners, Inc. <http://novus.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* For questions and comments about this product, please see the project page at:
*
* http://github.com/mongodb/casbah
*
*/
package com.mongodb.casbah
package gridfs
import com.mongodb.casbah.Imports._
import com.mongodb.casbah.gridfs.Imports._
import com.mongodb.casbah.commons.Logging
import com.mongodb.casbah.commons.conversions.scala._
import com.mongodb.DBObject
import com.mongodb.gridfs.{ GridFS => MongoGridFS, GridFSDBFile => MongoGridFSDBFile, GridFSFile => MongoGridFSFile, GridFSInputFile => MongoGridFSInputFile }
import java.io._
import scala.reflect._
import scala.collection.JavaConverters._
import com.github.nscala_time.time.Imports._
// todo - look into potential naming conflicts...
/**
* Companion object for GridFS.
* Entry point for creation of GridFS Instances.
*
* @since 1.0
*/
object GridFS extends Logging {
def apply(db: MongoDB) = {
log.info("Creating a new GridFS Entry against DB '%s', using default bucket ('%s')", db.name, MongoGridFS.DEFAULT_BUCKET)
new GridFS(new MongoGridFS(db.underlying))
}
def apply(db: MongoDB, bucket: String) = {
log.info("Creating a new GridFS Entry against DB '%s', using specific bucket ('%s')", db.name, bucket)
new GridFS(new MongoGridFS(db.underlying, bucket))
}
}
class GridFS protected[gridfs] (val underlying: MongoGridFS) extends Iterable[GridFSDBFile] with Logging {
log.info("Instantiated a new GridFS instance against '%s'", underlying)
type FileOp = GridFSFile => Unit
type FileWriteOp = GridFSInputFile => Unit
type FileReadOp = GridFSDBFile => Unit
implicit val db = underlying.getDB().asScala
def iterator = new Iterator[GridFSDBFile] {
val fileSet = files
def count() = fileSet.count
override def length() = fileSet.length
def numGetMores() = fileSet.numGetMores
def numSeen() = fileSet.numSeen
def curr() = new GridFSDBFile(fileSet.next().asInstanceOf[MongoGridFSDBFile])
def explain() = fileSet.explain
def next() = new GridFSDBFile(fileSet.next.asInstanceOf[MongoGridFSDBFile])
def hasNext: Boolean = fileSet.hasNext
}
/**
* loan
*
* Basic implementation of the Loan pattern -
* the idea is to pass a Unit returning function
* and a Mongo file handle, and work on it within
* a code block.
*
*/
protected[gridfs] def loan[T <: GenericGridFSFile](file: T)(op: T => Option[AnyRef]) = op(file)
/**
* Create a new GridFS File from a scala.io.Source
*
* Uses a loan pattern, so you need to pass a curried function which expects a GridFSInputFile
* as a parameter.
* It AUTOMATICALLY saves the GridFS file at it's end, so throw an exception if you want to fail.
* If you don't want automatic saving/loaning please see the createFile method instead.
* @see createFile
* @return The ID of the created File (Option[AnyRef])
*/
def apply(data: scala.io.Source)(op: FileWriteOp) = withNewFile(data)(op)
/**
* Create a new GridFS File from a Byte Array
*
* Uses a loan pattern, so you need to pass a curried function which expects a GridFSInputFile
* as a parameter.
* It AUTOMATICALLY saves the GridFS file at it's end, so throw an exception if you want to fail.
* If you don't want automatic saving/loaning please see the createFile method instead.
* @see createFile
* @return The ID of the created File (Option[AnyRef])
*/
def apply(data: Array[Byte])(op: FileWriteOp) = withNewFile(data)(op)
/**
* Create a new GridFS File from a java.io.File
*
* Uses a loan pattern, so you need to pass a curried function which expects a GridFSInputFile
* as a parameter.
* It AUTOMATICALLY saves the GridFS file at it's end, so throw an exception if you want to fail.
* If you don't want automatic saving/loaning please see the createFile method instead.
* @see createFile
* @return The ID of the created File (Option[AnyRef])
*/
def apply(f: File)(op: FileWriteOp) = withNewFile(f)(op)
/**
* Create a new GridFS File from a java.io.InputStream
*
* Uses a loan pattern, so you need to pass a curried function which expects a GridFSInputFile
* as a parameter.
* It AUTOMATICALLY saves the GridFS file at it's end, so throw an exception if you want to fail.
* If you don't want automatic saving/loaning please see the createFile method instead.
* @see createFile
* @return The ID of the created File (Option[AnyRef])
*/
def apply(in: InputStream)(op: FileWriteOp) = withNewFile(in)(op)
/**
* Create a new GridFS File from a java.io.InputStream and a specific filename
*
* Uses a loan pattern, so you need to pass a curried function which expects a GridFSInputFile
* as a parameter.
* It AUTOMATICALLY saves the GridFS file at it's end, so throw an exception if you want to fail.
* If you don't want automatic saving/loaning please see the createFile method instead.
* @see createFile
* @return The ID of the created File (Option[AnyRef])
*/
def apply(in: InputStream, filename: String)(op: FileWriteOp) = withNewFile(in, filename)(op)
/**
* createFile
*
* Creates a new file in GridFS
*
* TODO - Should the curried versions give the option to not automatically save?
*/
def createFile(data: scala.io.Source): GridFSInputFile = throw new UnsupportedOperationException("Currently no support for scala.io.Source")
def withNewFile(data: scala.io.Source)(op: FileWriteOp) = throw new UnsupportedOperationException("Currently no support for scala.io.Source")
def createFile(data: Array[Byte]): GridFSInputFile = underlying.createFile(data)
/**
* Loan pattern style file creation.
* @return The ID of the created File (Option[AnyRef])
*/
def withNewFile(data: Array[Byte])(op: FileWriteOp) = loan(createFile(data)) { fh =>
op(fh)
fh.save()
fh.validate()
Option(fh.id)
}
def createFile(f: File): GridFSInputFile = underlying.createFile(f)
/**
* Loan pattern style file creation.
* @return The ID of the created File (Option[AnyRef])
*/
def withNewFile(f: File)(op: FileWriteOp) = loan(createFile(f)) { fh =>
op(fh)
fh.save()
fh.validate()
Option(fh.id)
}
def createFile(in: InputStream): GridFSInputFile = underlying.createFile(in)
/**
* Loan pattern style file creation.
* @return The ID of the created File (Option[AnyRef])
*/
def withNewFile(in: InputStream)(op: FileWriteOp) = loan(createFile(in)) { fh =>
op(fh)
fh.save()
fh.validate()
Option(fh.id)
}
def createFile(in: InputStream, filename: String): GridFSInputFile = underlying.createFile(in, filename)
/**
* Loan pattern style file creation.
* @return The ID of the created File (Option[AnyRef])
*/
def withNewFile(in: InputStream, filename: String)(op: FileWriteOp) = loan(createFile(in, filename)) { fh =>
op(fh)
fh.save()
fh.validate()
Option(fh.id)
}
/** Find by query - returns a list */
def find[A <% DBObject](query: A) = underlying.find(query).asScala
/** Find by query - returns a single item */
def find(id: ObjectId): GridFSDBFile = underlying.find(id)
/** Find by query - returns a list */
def find(filename: String) = underlying.find(filename).asScala
def findOne[A <% DBObject](query: A): Option[GridFSDBFile] = {
underlying.findOne(query) match {
case null => None
case x => Some(x)
}
}
def findOne(id: ObjectId): Option[GridFSDBFile] = {
underlying.findOne(id) match {
case null => None
case x => Some(x)
}
}
def findOne(filename: String): Option[GridFSDBFile] = {
underlying.findOne(filename) match {
case null => None
case x => Some(x)
}
}
def bucketName = underlying.getBucketName
/**
* Returns a cursor for this filestore
* of all of the files...
*/
def files = { new MongoCursor(underlying.getFileList) }
def files[A <% DBObject](query: A) = { new MongoCursor(underlying.getFileList(query)) }
def remove[A <% DBObject](query: A) = underlying.remove(query)
def remove(id: ObjectId) = underlying.remove(id)
def remove(filename: String) = underlying.remove(filename)
}
@BeanInfo
abstract class GenericGridFSFile(override val underlying: MongoGridFSFile) extends MongoDBObject with Logging {
type DateType
def convertDate(in: AnyRef): DateType
override def iterator = underlying.keySet.asScala.map { k =>
k -> underlying.get(k)
}.toMap.iterator.asInstanceOf[Iterator[(String, AnyRef)]]
def save() { underlying.save() }
/**
* validate the object.
* Throws an exception if it fails
* @throws MongoException An error describing the validation failure
*/
def validate() { underlying.validate() }
def numChunks: Int = underlying.numChunks
def id = underlying.getId
def filename = Option(underlying.getFilename)
// todo - does mongo support mime magic? Should we pull some in here?
def contentType = Option(underlying.getContentType)
def length = underlying.getLength
def chunkSize = underlying.getChunkSize
def uploadDate: DateType = convertDate(underlying.get("uploadDate"))
def aliases = underlying.getAliases.asScala
def metaData: DBObject = underlying.getMetaData
def metaData_=[A <% DBObject](meta: A) = underlying.setMetaData(meta)
def md5: String = underlying.getMD5
override def toString = "{ GridFSFile(id=%s, filename=%s, contentType=%s) }".
format(id, filename, contentType)
}
@BeanInfo
class GridFSFile(_underlying: MongoGridFSFile) extends GenericGridFSFile(_underlying) {
type DateType = java.util.Date
def convertDate(in: AnyRef) = in match {
case d: java.util.Date => d
case j: DateTime => new java.util.Date(j.getMillis)
}
}
@BeanInfo
abstract class GenericGridFSDBFile protected[gridfs] (override val underlying: MongoGridFSDBFile) extends GenericGridFSFile(underlying) {
def inputStream = underlying.getInputStream
def source = scala.io.Source.fromInputStream(inputStream)
def writeTo(file: java.io.File) = underlying.writeTo(file)
def writeTo(out: java.io.OutputStream) = underlying.writeTo(out)
def writeTo(filename: String) = underlying.writeTo(filename)
override def toString = "{ GridFSDBFile(id=%s, filename=%s, contentType=%s) }".
format(id, filename, contentType)
}
@BeanInfo
class GridFSDBFile(_underlying: MongoGridFSDBFile) extends GenericGridFSDBFile(_underlying) {
type DateType = java.util.Date
def convertDate(in: AnyRef): DateType = in match {
case d: java.util.Date => d
case j: DateTime => new java.util.Date(j.getMillis)
}
}
@BeanInfo
abstract class GenericGridFSInputFile protected[gridfs] (override val underlying: MongoGridFSInputFile) extends GenericGridFSFile(underlying) {
def filename_=(name: String) = underlying.setFilename(name)
def contentType_=(cT: String) = underlying.setContentType(cT)
}
@BeanInfo
class GridFSInputFile(_underlying: MongoGridFSInputFile) extends GenericGridFSInputFile(_underlying) {
type DateType = java.util.Date
def convertDate(in: AnyRef): DateType = in match {
case d: java.util.Date => d
case j: DateTime => new java.util.Date(j.getMillis)
}
}