views:

633

answers:

3

Is there a succinct example of how to upload an image, resize it, store it in a database and then serve the image up using Lift?

I'm sure I could piece it together from the file upload, Java 2D API, Lift Mapper and Response APIs. But is there any example code I can follow to do it the 'correct' or recommended way?

+1  A: 

I did this for a Mapper field linked to s3 by creating a new MappedField. I also have a some code to resize, but haven't tested or deployed (so use with caution).

class MappedS3Image[T<:Mapper[T]](owner: T, val path:String, maxWidth: String, maxHeight:String) extends MappedString[T](owner, 36) {

  def url:String = MappedS3Image.fullImgPath(path, is)

  def setFromUpload(fileHolder: Box[FileParamHolder]) = {
      S3Sender.uploadImageToS3(path, fileHolder).map(this.set(_))
  }

  override def asHtml:Node = <img src={url} style={"max-width:" + maxWidth + ";max-height:"+maxHeight} />
  override def _toForm: Box[Elem] = Full(SHtml.fileUpload(fu=>setFromUpload(Full(fu))))

}


import java.awt.Image 
import java.awt.image.BufferedImage
import javax.imageio.ImageIO
import java.awt.Graphics2D
import java.awt.AlphaComposite

object ImageResizer {

    def resize(is:java.io.InputStream, maxWidth:Int, maxHeight:Int):BufferedImage = {
        val originalImage:BufferedImage = ImageIO.read(is)

        val height = originalImage.getHeight
        val width = originalImage.getWidth

        if (width <= maxWidth && height <= maxHeight)
            originalImage
        else {
            var scaledWidth:Int = width
            var scaledHeight:Int = height
            val ratio:Double = width/height
            if (scaledWidth > maxWidth){
                scaledWidth = maxWidth
                scaledHeight = (scaledWidth.doubleValue/ratio).intValue
            }
            if (scaledHeight > maxHeight){
                scaledHeight = maxHeight
                scaledWidth = (scaledHeight.doubleValue*ratio).intValue
            }
            val scaledBI = new BufferedImage(scaledWidth, scaledHeight,  BufferedImage.TYPE_INT_RGB)
            val g = scaledBI.createGraphics
            g.setComposite(AlphaComposite.Src)
            g.drawImage(originalImage, 0, 0, scaledWidth, scaledHeight, null);
            g.dispose
            scaledBI
        }
    }
}
Jon Hoffman
A: 

Here is a good example of an app that people can upload to...

http://github.com/dpp/imagine

They also recently added some code to resize images.

Randin
+1  A: 

The other answer nicely describes how to resize the image and store a reference to the file on the file system.

If you want to use the lift mapper to store the actual file contents, you have to create your custom model object, and define a binary field on it. Try something like this:

package code {
package model {


import _root_.net.liftweb.mapper._
import _root_.net.liftweb.util._
import _root_.net.liftweb.common._


// singleton object which manipulates storing of Document instances
object Document extends Document with KeyedMetaMapper[Long, Document] {
}



class Document extends KeyedMapper[Long, Document] {
  def getSingleton = Document
  def primaryKeyField = id

  object id extends MappedLongIndex(this)

  object name extends MappedString(this, 20) {
    override def displayName = "Name"
    override def writePermission_? = true
  }

  object content extends MappedBinary(this) {
    override def displayName = "Content"
    override def writePermission_? = true
  }
}



}
}

Then, in bootstrap class, add this Document at the end:

Schemifier.schemify(true, Schemifier.infoF _, User, Document)

Voila. Using Document save (new Document) stores it into database. A new Document's fields can be set using the set method. Try playing with delete_!, find, findAll methods of the Document singleton to delete or find it in the database. It should be straightforward from this point on.

Finally, to display the image, you can override Lift's dispatching rules (in bootstrap class, Boot.scala). Try playing around with this example which overrides the rules for pdf requests:

def getFile(filename: String): Option[Document] = {
  val alldocs = Document.findAll()
  alldocs.find(_.name.get == filename)
}

LiftRules.statelessDispatchTable.append {
  case Req("file" :: name :: Nil, "pdf", GetRequest) =>
    () =>
    println("Got request for: " + name + ".pdf")
    for {
      stream <- tryo(
        getFile(name + ".pdf") map { 
          doc => new java.io.ByteArrayInputStream(doc.content.get)
        } getOrElse null
      )
      if null ne stream
    } yield StreamingResponse(stream,
                              () => stream.close,
                              stream.available,
                              List("Content-Type" -> "application/pdf"),
                              Nil,
                              200)
}
axel22