All Downloads are FREE. Search and download functionalities are using the official Maven repository.

se.digiplant.scalr.ScalrResAssets.scala Maven / Gradle / Ivy

There is a newer version: 1.1.2
Show newest version
package se.digiplant.scalr

import api.Resizer
import play.api._
import play.api.mvc._
import play.api.libs._
import org.joda.time.format.{DateTimeFormatter, DateTimeFormat}
import org.joda.time.DateTimeZone
import collection.JavaConverters._
import java.io.File

import play.api.Play.current

object ScalrResAssets extends Controller {

  private val timeZoneCode = "GMT"

  //Dateformatter is immutable and threadsafe
  private val df: DateTimeFormatter = DateTimeFormat
    .forPattern("EEE, dd MMM yyyy HH:mm:ss '" + timeZoneCode + "'")
    .withLocale(java.util.Locale.ENGLISH)
    .withZone(DateTimeZone.forID(timeZoneCode))

  //Dateformatter is immutable and threadsafe
  private val dfp: DateTimeFormatter = DateTimeFormat
    .forPattern("EEE, dd MMM yyyy HH:mm:ss")
    .withLocale(java.util.Locale.ENGLISH)
    .withZone(DateTimeZone.forID(timeZoneCode))

  private val parsableTimezoneCode = " " + timeZoneCode

  /**
   * Resize and cache images stored in play-res
   * @param fileuid Fileuid of file stored in play-res
   * @param width Width of resized image
   * @param height Height of resized image
   * @param mode AUTOMATIC, FIT_EXACT, FIT_TO_WIDTH, FIT_TO_HEIGHT
   * @param source play-res source
   * @return A resized image
   */
  def at(fileuid: String, width: Int, height: Int = 0, mode: String = "automatic", source: String = "default"): Action[AnyContent] = Action { request =>

    val modeEnum: Resizer.Mode = Resizer.Mode.valueOf(mode.toUpperCase)

    def parseDate(date: String): Option[java.util.Date] = {
      try {
        //jodatime does not parse timezones, so we handle that manually
        val d = dfp.parseDateTime(date.replace(parsableTimezoneCode, "")).toDate
        Some(d)
      } catch {
        case _: Exception => None
      }
    }

    api.Scalr.getRes(fileuid, source, width, height, modeEnum, Resizer.Method.ULTRA_QUALITY).map { resizedImage =>
      request.headers.get(IF_NONE_MATCH).flatMap {
        ifNoneMatch => etagFor(resizedImage).filter(_ == ifNoneMatch)
      }.map(_ => NotModified).getOrElse {
        request.headers.get(IF_MODIFIED_SINCE).flatMap(parseDate).flatMap {
          ifModifiedSince => lastModifiedFor(resizedImage).flatMap(parseDate).filterNot(lastModified => lastModified.after(ifModifiedSince))
        }.map(_ => NotModified.withHeaders(
          DATE -> df.print({ new java.util.Date }.getTime)
        )).getOrElse {

          val response = Ok.sendFile(resizedImage, inline = true)

          // Add Etag if we are able to compute it
          val taggedResponse = etagFor(resizedImage).map(etag => response.withHeaders(ETAG -> etag)).getOrElse(response)
          val lastModifiedResponse = lastModifiedFor(resizedImage).map(lastModified => taggedResponse.withHeaders(LAST_MODIFIED -> lastModified)).getOrElse(taggedResponse)

          // Add Cache directive if configured

          /*val cachedResponse = lastModifiedResponse.withHeaders(CACHE_CONTROL -> {
            Play.mode match {
              case Mode.Prod => Play.configuration.getString("assets.defaultCache").getOrElse("max-age=3600")
              case _ => "no-cache"
            })
          })*/

          lastModifiedResponse

        }: Result
      }
    }.getOrElse {
      NotFound
    }
  }

  /**
   * Resizes and crops and cache images stored in play-res
   * @param fileuid Fileuid of file stored in play-res
   * @param width Width of resized image
   * @param height Height of resized image
   * @param source play-res source
   * @return A resized image
   */
  def crop(fileuid: String, width: Int, height: Int = 0, source: String = "default") = at(fileuid, width, height, mode = "crop", source)

  // Last modified
  private val lastModifieds = (new java.util.concurrent.ConcurrentHashMap[String, String]()).asScala

  private def lastModifiedFor(file: File): Option[String] = {
    lastModifieds.get(file.getName).filter(_ => Play.isProd).orElse {
      val lastModified = df.print({
        new java.util.Date(file.lastModified).getTime
      })
      lastModifieds.put(file.getName, lastModified)
      Some(lastModified)
    }
  }

  // Etags
  private val etags = (new java.util.concurrent.ConcurrentHashMap[String, String]()).asScala

  private def etagFor(file: File): Option[String] = {
    etags.get(file.getName).filter(_ => Play.isProd).orElse {
      val maybeEtag = lastModifiedFor(file).map(_ + " -> " + file.getName).map("\"" + Codecs.sha1(_) + "\"")
      maybeEtag.foreach(etags.put(file.getName, _))
      maybeEtag
    }
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy