Make URLs for assets cache-proof

https://www.pivotaltracker.com/story/show/113448149

This commit adds a query string to assets URLs which is generated from a hash
of the file contents. When asset files are changed they will now be served from
a different URL, which means they wont be loaded from browser cache.

This is similar to how GOV.UK template adds its version number as a querystring
parameter for its assets.

This is mostly copied from Digital Marketplace utils:
https://github.com/alphagov/digitalmarketplace-utils/pull/102

They have it in a shared codebase, we only have one frontend app so don’t need
to do that.

Usage in a template:
``` jinja
{{ asset_fingerprinter.get_url('stylesheets/application.css') }}
```

Output:
```
static/stylesheets/application.css?418e6f4a6cdf1142e45c072ed3e1c90a
```
This commit is contained in:
Chris Hill-Scott
2016-02-10 15:47:00 +00:00
parent 5fd8ca492d
commit 2f0cc99610
4 changed files with 150 additions and 6 deletions

View File

@@ -0,0 +1,46 @@
import hashlib
import codecs
class AssetFingerprinter(object):
"""
Get a unique hash for an asset file, so that it doesn't stay cached
when it changes
Usage:
in the application
template_data.asset_fingerprinter = AssetFingerprinter()
where template data is how you pass variables to every template.
in template.html:
{{ asset_fingerprinter.get_url('stylesheets/application.css') }}
* 'app/static' is assumed to be the root for all asset files
"""
def __init__(self, asset_root='/static/', filesystem_path='app/static/'):
self._cache = {}
self._asset_root = asset_root
self._filesystem_path = filesystem_path
def get_url(self, asset_path):
if asset_path not in self._cache:
self._cache[asset_path] = (
self._asset_root +
asset_path +
'?' +
self.get_asset_fingerprint(self._filesystem_path + asset_path)
)
return self._cache[asset_path]
def get_asset_fingerprint(self, asset_file_path):
return hashlib.md5(
self.get_asset_file_contents(asset_file_path).encode('utf-8')
).hexdigest()
def get_asset_file_contents(self, asset_file_path):
with codecs.open(asset_file_path, encoding='utf-8') as asset_file:
contents = asset_file.read()
return contents