Gzipping assets

739 views
Skip to first unread message

johanandren

unread,
Aug 3, 2012, 5:05:35 AM8/3/12
to play-fr...@googlegroups.com
Custom SBT stuff for gzipping the generated css and js in a play project. Add this to project/Build.scala:

object Gzip {

  val gzippableAssets = SettingKey[PathFinder]("gzippable-assets", "Defines the files to gzip")

  // custom task to gzip static assets, will be available from the Assets controller
  // when run in prod mode if the client accepts gzip
  val gzipAssets = TaskKey[Seq[File]]("gzip-assets", "GZIP all assets")
  lazy val gzipAssetsSetting = gzipAssets <<= gzipAssetsTask
  lazy val gzipAssetsTask = (gzippableAssets, streams) map {
    case (finder: PathFinder, s: TaskStreams) => {
      finder.get.map { file =>
        val gzTarget = new File(file.getAbsolutePath + ".gz")
        IO.gzip(file, gzTarget)
        s.log.info("Compressed " + file.getName + " " + file.length / 1000 + " k => " + gzTarget.getName + " " + gzTarget.length / 1000 + " k")
        gzTarget
      }
    }
  }
}

and then include in your project settings
 
val main = PlayProject(appName, appVersion, appDependencies, mainLang = SCALA).settings(
    // Add your own project settings here
    ...
    // set up gzip of assets
    gzippableAssets <<= (resourceManaged in (ThisProject))(dir => ((dir ** "*.js") +++ (dir ** "*.css"))),
    gzipAssetsSetting
    ...
)


Now you can run "gzip-assets" in your play console to gzip any generated asset files. 

Haven't figured out where or when to automatically call it yet though.

johanandren

unread,
Aug 3, 2012, 5:21:42 AM8/3/12
to play-fr...@googlegroups.com
Sorry, forgot the both subject prefix, its [2.0]

And I discovered that I cannot get play to serve those gzipped files, even though i add the gzipAssetsSetting to the
resourceGenerators in (ThisProject, Compile) <+= gzipAssetsTask

and I see the files beeing compressed on every assets-regeneration, but running "play start" still does not make the Assets controller serve them.

Any clues?

johanandren

unread,
Aug 3, 2012, 5:25:27 AM8/3/12
to play-fr...@googlegroups.com
Oh, never mind, it seems as though the new developer view in safari tricked me, it does serve the gzipped files!

Eric Jain

unread,
Aug 5, 2012, 5:49:29 PM8/5/12
to play-fr...@googlegroups.com
Very useful! This is one of these things that I expected Play would do out of the box, but it didn't... Have you considered contributing this code to the Play framework (especially if you can figure out how to run it as part of the build)?

Eric Jain

unread,
Sep 4, 2012, 7:46:37 PM9/4/12
to play-fr...@googlegroups.com
Just noticed that after upgrading to 2.1-SNAPSHOT, the gzipping is repeated several times for each file for each request. Any ideas why that could be?

Doesn't really make sense to run this task except during staging, anyway; not sure how to accomplish that?

virtualeyes

unread,
Sep 13, 2012, 7:09:55 AM9/13/12
to play-fr...@googlegroups.com
Exactly, how to get this working in production when you actually need it, ideally as part of the build process.

Guillaume suggests in another thread that it is much easier (and faster) to gzip files via reverse proxy, which I assume to mean a front end web server like Apache running mod_pagespeed; basically offload the gzip'ing task elsewhere.

As it stands, Play provides expires and ETag headers, so after the initial request, pages tend to load very quickly, even behind a load balancer.

The primary missing ingredient is gzip'ing content.

To put in perspective, a legacy custom LAMP stack app loads far more quickly than the same app converted Play, solely because html page content is not (yet) gzip'd by Play.

If anyone has a fairly straightforward means to have front end Apache gzip content with Play back end, please do tell (a bit wary of mod_pagespeed at this point).

AFAIK, Apache proxies to Play back end and gets out of the way (i.e. does not apply post-request processing via mod_deflate et al)

virtualeyes

unread,
Sep 13, 2012, 9:48:29 AM9/13/12
to play-fr...@googlegroups.com
Oh wow, now I see why Guillaume suggested that gzip'ing content via reverse proxy is much easier.

Adding something like the following to <Location> proxy block in Apache:
AddOutputFilterByType DEFLATE text/css application/x-javascript text/html text/plain text/xml image/x-icon
SetOutputFilter DEFLATE

results in sizable reduction in page load times as css, js, and html are all gzip'd on the way out to client -- no need to do a thing in Play ;-)

I suppose it might be worth it to gzip static js, css, and html Play files at build time (assuming the supplied informal plugin above does just this), might offload some CPU time from Apache gzip'ing the kitchen sink...but then again, Apache is not serving a single file from its own file system (Play is doing all the heavy lifting on master/slave servers on the LAN), so why not put it to work beyond load balancing and providing SSL ;-)

Have not tried it yet, but James Ward posted an excellent article on getting Play to work with Cloud Front:
http://www.jamesward.com/2012/08/08/edge-caching-with-play2-heroku-cloudfront

I'll add that in later, for now, with Play supplied expires and ETags headers, and Apache gzip'ing outbound content, we are already doing quite nicely...



On Wednesday, September 5, 2012 1:46:37 AM UTC+2, Eric Jain wrote:

Rui Ferreira

unread,
Aug 8, 2013, 10:49:47 AM8/8/13
to play-fr...@googlegroups.com
Great tip. Thanks a lot! I ran into this problem when running the app in production.
However, it doesn't package the gzipped assets when I run a play dist or play stage. Do you know what I need to change in the Build.scala to accomplish this?

Rui Ferreira

unread,
Aug 8, 2013, 12:05:52 PM8/8/13
to play-fr...@googlegroups.com
Reply all
Reply to author
Forward
0 new messages