使用ReactiveMongo和GridFS在PlayFramework中调整上传图像的大小

时间:2014-04-18 22:00:08

标签: file-upload playframework resize gridfs reactivemongo

我正在使用Play Framework和ReactiveMongo DB来存储GridFS中的上传文件。这是代码:

  def saveAttachment(id: String) = Action.async(gridFSBodyParser(gridFS)) { request =>
  // here is the future file!
    val futureFile = request.body.files.head.ref
    // when the upload is complete, we add the article id to the file entry (in order to find the attachments of the article)
    val futureUpdate = for {
      file <- futureFile
      // here, the file is completely uploaded, so it is time to update the user
      updateResult <- {
        gridFS.files.update(
          BSONDocument("_id" -> file.id),
          BSONDocument("$set" -> BSONDocument("user" -> BSONObjectID(id)),
            "$set" -> BSONDocument("size" -> "original")))
      }
    } yield updateResult

    futureUpdate.map {
      case _ => Ok("xx")
    }.recover {
      case e => InternalServerError(e.getMessage())
    }
  }

如何使用属性&#34; size&#34;添加另一个文件? - &GT; &#34;拇指&#34;使用相同的上传图片,但重新调整为小拇指? 我这里有两个问题:

  • 如何在一次上传中将2个文件存储在GridFS中?
  • 如何在存储图像之前调整图像大小?

感谢您的回答。这是一个很好的方向。我正在寻找GridFS的解决方案。以下是我最终的结果:

  def saveAttachment(id: String) = Action.async(gridFSBodyParser(gridFS)) { request =>
  // here is the future file!
    val futureFile = request.body.files.head.ref
    // when the upload is complete, we add the article id to the file entry (in order to find the attachments of the article)
    val futureUpdate = for {
      file <- futureFile
      // here, the file is completely uploaded, so it is time to update the user
      updateResult <- {
        gridFS.files.update(
          BSONDocument("_id" -> file.id),
          BSONDocument("$set" -> BSONDocument("metadata" ->
                        BSONDocument("user" -> BSONObjectID(id),
                                      "size" -> "original"))))


        val iterator = gridFS.enumerate(file).run(Iteratee.consume[Array[Byte]]())
        iterator.flatMap {
          bytes => {
            // Create resized image
            val enumerator: Enumerator[Array[Byte]] = Enumerator.outputStream(
              out => {
                Image(bytes).bound(120, 120).writer(Format.JPEG).withCompression(90).write(out)
              }
            )

            val data = DefaultFileToSave(
              filename = file.filename,
              contentType = file.contentType,
              uploadDate = Some(DateTime.now().getMillis),
              metadata = file.metadata ++ BSONDocument(
                "user" -> BSONObjectID(id),
                "size" -> "thumb"
              )
            )

            Logger.warn(s"Saving resized image: [id=$id, metadata=${data.metadata}}]")
            gridFS.save(enumerator, data).map {
              image => Some(image)
            }
          }
        }
      }
    } yield updateResult

    futureUpdate.map {
      case _ => Ok("xx")
    }.recover {
      case e => InternalServerError(e.getMessage())
    }
  }

1 个答案:

答案 0 :(得分:2)

解决方案非常简单,我使用scrimage进行图像处理,这是我的控制器:

// imports omitted

def upload = Authenticated.async(parse.multipartFormData) { request =>

  // some plumbing omitted

  request.body.file("photo") match {
    // validations omitted
    case Some(photo) =>
      val fileToSave = DefaultFileToSave(photo.filename, photo.contentType)
      val resizedFile = Image(photo.ref.file).fitToWidth(120).write
      val enumerator = Enumerator(resizedFile)
      gfs.save(enumerator, fileToSave) map {
        case file =>
          val id = file.id.asInstanceOf[BSONObjectID].stringify
          Ok(obj("files" -> Seq(obj(
            "id" -> id,
            "name" -> file.filename,
            "size" -> file.length,
            "url" -> routes.Photos.photo(id).url,
            "thumbnailUrl" -> routes.Photos.photo(id).url,
            "deleteUrl" -> "",
            "deleteType" -> "DELETE"
          ))))
      } recover {
        case e =>
          Logger.error(e.toString)
          InternalServerError("upload failed")
      }
    }
  }
}

请注意调整大小部分。

您可以解决的第一个问题,例如,只需复制基础文件并使用调整大小的副本进行第二次save调用。我手边的代码没有完整的解决方案,如果您需要进一步的帮助,请随时询问详细信息。