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
Apr 11, 2024Technology
Test Analysis at the Feature Level

In the previous article, we learned about test analysis for a product in general, and now we are ready to go further and look at test analysis for specific features.

Sep 26, 2023TechnologyBusiness
13 Web Development Innovations to Adopt in the Next Year

Web development has undergone significant changes over the past five years due to a combination of technological advancements and changing user needs. Let's look at the drivers of these changes and the key technologies that have played a decisive role.

Feb 12, 2020Technology
5 Best Payment Gateways For 2020

We reviewed the best payment gateways in 2020. Here’s our comparison of their features, advantages, and disadvantages.

Jun 25, 2011Technology
Ajax blocks in Django

Quite often we have to write paginated or filtered blocks of information on page. I created a decorator that would automate this process.

Mar 6, 2010Technology
Ajax form validation

There was a task to submit form with ajax, with server side validation of course. Obvious solution is to do validation and return json with erros. I didn't like idea of writing separate view for validation and then inserting errors in form html on client side. Especially since I already had a generic template for django form with errors display. In this article I'll describe how I solved the task.

Feb 18, 2010Technology
Business logic in models

In my recent project there was a lot of data business logic, so I had to organize this code somehow. In this article I'll describe a few hints on how to it.