As for Solution #1
If you are using ReactiveMongo, there is an example project, with composite server response, consisting of files and json
https://github.com/sgodbillon/reactivemongo-demo-app/blob/master/app/controllers/Application.scala
def showEditForm(id: String) = Action.async {
val objectId = new BSONObjectID(id)
// get the documents having this id (there will be 0 or 1 result)
val futureArticle = collection.find(BSONDocument("_id" -> objectId)).one[Article]
// ... so we get optionally the matching article, if any
// let's use for-comprehensions to compose futures (see http://doc.akka.io/docs/akka/2.0.3/scala/futures.html#For_Comprehensions for more information)
for {
// get a future option of article
maybeArticle <- futureArticle
// if there is some article, return a future of result with the article and its attachments
result <- maybeArticle.map { article =>
import reactivemongo.api.gridfs.Implicits.DefaultReadFileReader
// search for the matching attachments
// find(...).toList returns a future list of documents (here, a future list of ReadFileEntry)
gridFS.find(BSONDocument("article" -> article.id.get)).collect[List]().map { files =>
val filesWithId = files.map { file =>
file.id.asInstanceOf[BSONObjectID].stringify -> file
}
Ok(views.html.editArticle(Some(id), Article.form.fill(article), Some(filesWithId)))
}
}.getOrElse(Future(NotFound))
} yield result
}
You can use it as a reference implementation.
As for Solution #2
You should be able to store additional data in DefaultFileToSave.metadata, and query afterwards mongo with { "metadata.user" : "user"} (Look at Query on MongoDB GridFS metadata (Java))
Another Solution
Keep files & your meta information as independent entities, and manage them independently, it would be easier to extend them in future.
For GridFS upload functionality (https://github.com/ReactiveMongo/Play-ReactiveMongo)
def upload = Action(gridFSBodyParser(gridFS)) { request =>
// here is the future file!
val futureFile: Future[ReadFile[BSONValue]] = request.body.files.head.ref
futureFile.map { file =>
// do something
Ok
}.recover {
case e: Throwable => InternalServerError(e.getMessage)
}
}
To expand on previous, in terms of endpoints, as an example
- /video/:id/file - your GridFS file endpoint
- /video/:id/meta - your meta information endpoint (what ever you need it to be)
- /video/:id (optional) - combined file & meta response.