Back
Apr 3, 2011

Sprite cache invalidation

When we use css-sprites it's important to make browser cache them for longest period possible. On other hand, we need to refresh them when they are updated. This is especially visible when all icons are stored in single sprite. When it's outdated - entire site becomes ugly.

To solve this task I've implemented this small script that adds file's hash to url:

background-image: url(images/icons.png);
background-image: url(images/icons.png?a3844c660);
#!/usr/bin/python

import os.path
import re
import hashlib


def file_hash(filename, block_size=2**20):
    md5 = hashlib.md5()
    with open(filename, 'rb') as f:
        for data in iter(lambda: f.read(block_size), ''):
            md5.update(data)
    return md5.hexdigest()


def find_replace(match, basedir):
    match = match.replace("'", '')
    if '?' in match or '#' in match or match.startswith('http://'):
        print 'skipping: ', match
        return None

    filename = os.path.join(basedir, match)
    return '%s?%s' % (match, file_hash(filename)[:10])


def insert_hash(filename, debug=False):
    basedir = os.path.dirname(filename)
    css = open(filename).read()
    matches = re.findall(r"url\((.*?)\)", css)
    replacements = dict([
        (match, find_replace(match, basedir))
        for match in matches
    ])

    if debug:
        for key, value in replacements.iteritems():
            print key, '    =>    ', value
    else:
        for match, repl in replacements.iteritems():
            if repl:
                css = css.replace(match, repl)

        out = open(filename, 'w')
        out.write(css)
        out.close()


if __name__ == '__main__':
    from optparse import OptionParser
    usage = 'usage: %prog filename.css'

    parser = OptionParser(usage=usage)
    parser.add_option("", "--debug", action="store_true", dest="debug", default=False,
                      help="When set, just output list of substitutions.")

    (options, args) = parser.parse_args()

    if len(args) < 1:
        print 'You must provide file name.'

    insert_hash(os.path.realpath(args[0]), options.debug)

Update

This is pretty old article. Now it's better to use something integrated to your build process - grunt, webassets, etc.

Subscribe for the news and updates

More thoughts
Mar 26, 2025Technology
Common Mistakes and Recommendations for Test Cases

The article highlights common test case mistakes, offers ways to fix them, and provides practical tips to improve and optimize test cases.

Jun 14, 2017Technology
How to Deploy a Django Application on Heroku?

In this article I'll show you how to deploy Django with Celery and Postgres to Heroku.

Mar 2, 2017Technology
API versioning with django rest framework?

We often handling API server updates including backwards-incompatible changes when upgrading web applications. At the same time we update the client part, therefore, we did not experience any particular difficulties.

Dec 1, 2016Technology
How to Use Django & PostgreSQL for Full Text Search

For any project there may be a need to use a database full-text search. We expect high speed and relevant results from this search. When we face such problem, we usually think about Solr, ElasticSearch, Sphinx, AWS CloudSearch, etc. But in this article we will talk about PostgreSQL. Starting from version 8.3, a full-text search support in PostgreSQL is available. Let's look at how it is implemented in the DBMS itself.

Oct 3, 2016Technology
How to include JQuery plugins in Angular 2 running via webpack

Learn more about how to include jquery plugins in angular 2 running via webpack. Our tutorial is perfect for Angular beginners.

Feb 18, 2010Technology
User profiles with inheritance in Django

Usually users' profiles are stored in single model. When there are multiple user types, separation is made by some field like user_type.Situation is a little more complicated when different data is needed for each user type.In this article I'll describe how I solve this task.