使Django中静态文件的浏览器缓存无效

58wvjzkj  于 2023-02-10  发布在  Go
关注(0)|答案(3)|浏览(126)

在Django中我们有ManifestStaticFilesStorage用于缓存静态文件,但它在Django和浏览器之间工作,但我希望在用户和浏览器之间正确缓存。
想要:每次静态文件更改时,文件哈希值都会重新计算,浏览器缓存无效,用户可以看到新的静态文件,而无需F5和运行--collectstatic --no-input
我的代码现在不工作:settings.py

STATICFILES_STORAGE = 'auth.utils.HashPathStaticFilesStorage'

CACHES = {
    'staticfiles': {
        'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
        'LOCATION': 'staticfiles',
        'TIMEOUT': 3600 * 24 * 7,
        'MAX_ENTRIES': 100,
    }
}

auth.utils.py

# -*- coding: utf-8 -*-

import time

from hashlib import sha384

from django.conf import settings
from django.core.cache import cache
from django.contrib.staticfiles.storage import ManifestStaticFilesStorage

try:
    ACCURACY = settings.STATICFILES_HASH_ACCURACY
except AttributeError:
    ACCURACY = 12

try:
    KEY_PREFIX = settings.STATICFILES_HASH_KEY_PREFIX
except AttributeError:
    KEY_PREFIX = 'staticfiles_hash'

class HashPathStaticFilesStorage(ManifestStaticFilesStorage):
    """A static file storage that returns a unique url based on the contents
    of the file. When a static file is changed the url will also change,
    forcing all browsers to download the new version of the file.

    The uniqueness of the url is a GET parameter added to the end of it. It
    contains the first 12 characters of the SHA3 sum of the contents of the
    file.

    Example: {% static "image.jpg" %} -> /static/image.jpg?4e1243

    The accuracy of the hash (number of characters used) can be set in
    settings.py with STATICFILES_HASH_ACCURACY. Setting this value too low
    (1 or 2) can cause different files to get the same hash and is not
    recommended. SHA3 hashes are 40 characters long so all accuracy values
    above 40 have the same effect as 40.

    The values can be cached for faster performance. All keys in the cache have
    the prefix specified in STATICFILES_HASH_KEY_PREFIX in setings.py. This
    value defaults to 'staticfiles_hash'
    """

    @property
    def prefix_key(self):
        return "%s:%s" % (KEY_PREFIX, 'prefix')

    def invalidate_cache(self, nocache=False):
        """Invalidates the cache. Run this when one or more static files change.
        If called with nocache=True the cache will not be used.
        """
        value = int(time.time())
        if nocache:
            value = None
        cache.set(self.prefix_key, value)

    def get_cache_key(self, name):
        hash_prefix = cache.get(self.prefix_key)
        if not hash_prefix:
            return None
        key = "%s:%s:%s" % (KEY_PREFIX, hash_prefix, name)
        return key

    def set_cached_hash(self, name, the_hash):
        key = self.get_cache_key(name)
        if key:
            cache.set(key, the_hash)

    def get_cached_hash(self, name):
        key = self.get_cache_key(name)
        if not key:
            return None
        the_hash = cache.get(key)
        return the_hash

    def calculate_hash(self, name):
        path = self.path(name)
        try:
            the_file = open(path, 'rb')
            the_hash = sha384(the_file.read()).hexdigest()[:ACCURACY]
            the_file.close()
        except IOError:
            return ""
        return the_hash

    def get_hash(self, name):
        the_hash = self.get_cached_hash(name)
        if the_hash:
            return the_hash
        the_hash = self.calculate_hash(name)
        self.set_cached_hash(name, the_hash)
        return the_hash

    def url(self, name):
        base_url = super(HashPathStaticFilesStorage, self).url(name)
        the_hash = self.get_hash(name)
        if "?" in base_url:
            return "%s&%s" % (base_url, the_hash)
        return "%s?%s" % (base_url, the_hash)
dtcbnfnu

dtcbnfnu1#

我只是用一个非常简单的想法

<img src="{{ company.logo.url }}?v={% now 'U' %}" />

使用?v=强制版本,并将版本设置为当前时间戳{% now 'U' %},以便它随每个请求而更改

qij5mzcb

qij5mzcb2#

避免用户不得不重新加载页面以获取新鲜静态内容的一种常见且简单的方法是在HTML标记中的静态文件的包含内容中附加一些可变值,如下所示:

<script src="{% static 'js/library.js' %}?{{ version }}"></script>

以这种方式,当变量 version 采用不同的值时,浏览器被强制从服务器下载静态文件的新版本。
您可以使用自定义上下文处理器设置 version,例如从设置中阅读项目版本。

from django.conf import settings

def version(request):
    return {
        'version': settings.VERSION
    }

如果你使用git作为VCS,另一种方法是将项目的最后一个提交哈希值写入一个文件中,然后将修改推送到服务器,该文件应该是Python可读的格式,这样你就可以使用git提交哈希值作为之前提到的 version 变量,你可以使用GIT post-receive hook

#!/bin/bash

WORKDIR=/path/to/project/
VERSION_MODULE=${WORKDIR}django_project/project/version.py

# for every branch which has been pushed
while read oldrev newrev ref
do
    # if branch pushed is master, update version.py file in the django project
    if [[ $ref =~ .*/master$ ]]; then
        GIT_WORK_TREE=$WORKDIR git checkout -f master
        echo "GIT_REF = 'master'" > $VERSION_MODULE
        echo "GIT_REV = '$newrev'" >> $VERSION_MODULE
    fi
done

则上下文处理器可以是:

from project.version import GIT_REV

def version(request):
    return {
        'version': GIT_REV[:7]
    }
vsaztqbk

vsaztqbk3#

超级简单的想法:修改Django设置中的STATIC_URL变量。

STATIC_URL = /static/     # before
STATIC_URL = /static/v2/  # after

这将改变 * 所有 * 静态文件的路径,迫使浏览器重新加载内容。

相关问题