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
Sep 8, 2023Technology
Smooth React virtual scroll with fixed rows/columns

One of our ongoing projects, Neptyne, introduces an Excel-like grid written in React. We used a library to apply virtual scroll to it, but we stumbled upon a problem with fixed rows and columns inside the grid. Here I would like to describe this problem, how it occurs, and how we handled it.

Jul 21, 2022Technology
Codemirror: unit-testing codemirror react components

One of our recent projects includes the functionality of an inline code editor. This code editor needed to be highly extensible and have custom features. To address this, we chose Codemirror v6 due to its peculiar architecture - it is highly customizable, and all the additional features are provided into codemirror engine as Extension objects.

May 12, 2022Technology
Increasing performance by using proper query structure

Earlier in our previous article "Improve efficiency of your SELECT queries" we discussed ways to profile and optimize the performance of SELECT queries. However, to write complex yet efficient SQL queries, there is a thing to remember about.

Jun 1, 2018Technology
Site search organization: basic concepts

Now it's time to get acquainted with Elasticsearch. This NoSQL database is used to store logs, analyze information and - most importantly - search.

Jan 12, 2017Technology
Making Custom Report Tables Using AngularJS and Django

In this article I will tell you how to create an interactive interface with a widely customized visual look and different filtering to view reports.

Dec 11, 2016Technology
Auto WebSocket Reconnection with RxJS (with Example)

In this RxJS tutorial article, we will focus on restoring the websocket connection when using RxJS library.