import os.path import contextlib import hashlib from flask import Flask from flask.helpers import safe_join # Injects an "h" parameter on the URLs of static files that contains a hash of # the file. This allows the use of aggressive cache settings on static files, # while ensuring that content changes are reflected immediately due to the # changed URLs. Hashes are cached in-memory and only checked for updates when # the file modtime changes. class AddStaticFileHashFlask(Flask): def __init__(self, *args, **kwargs): super(AddStaticFileHashFlask, self).__init__(*args, **kwargs) self._file_hash_cache = {} def inject_url_defaults(self, endpoint, values): super(AddStaticFileHashFlask, self).inject_url_defaults(endpoint, values) if "filename" not in values: return if values["filename"].lower().endswith("ico"): return if "static" not in endpoint: return static_folder = self.static_folder if "." in endpoint: endpoint = endpoint.split(".") if self.blueprints and self.blueprints.get(endpoint[0]) is not None: static_folder = self.blueprints.get(endpoint[0]).static_folder else: return filepath = safe_join(static_folder, values["filename"]) if os.path.isfile(filepath): cache = self._file_hash_cache.get(filepath) mtime = os.path.getmtime(filepath) if cache != None: cached_mtime, cached_hash = cache if cached_mtime == mtime: values["h"] = cached_hash return h = hashlib.md5() with contextlib.closing(open(filepath, "rb")) as f: h.update(f.read()) h = h.hexdigest() self._file_hash_cache[filepath] = (mtime, h) values["h"] = h app = AddStaticFileHashFlask(__name__) # ...