2012-02-03 67 views
24

我正在與靜態文件和更新問題的一些通用的解決方案,它Django的靜態文件版本

例如:可以說有網站與/static/styles.css文件 - 和網站用於很長一段時間 - 所以很多遊客在瀏覽器

在此css文件現在我們做的改變,並在服務器上更新緩存的這個文件,但一些用戶仍然有舊版本(儘管服務器返回的修改日期)

顯而易見的解決方案 - 將一些版本添加到文件/static/styles.css?v=1.1

但在這種情況下,開發人員必須跟蹤這個文件的更改和手動增加版本

解決方案2 - 計算文件的MD5哈希並添加到URL /static/styels.css/?v={mdp5hashvalue}

看起來好多了,但是MD5應計算一些如何自動..

他們可能的方式我看到它 - 創建一些模板標籤這樣

{% static_file "style.css" %} 

這會使

<link src="/static/style.css?v=md5hash"> 

,但我不希望這個標記來計算每個頁面加載MD5,我不希望存儲在Django緩存哈希,因爲那時我們會有更新文件後清除..

有什麼想法?

+1

已經有很多針對這個問題的解決方案,從那裏開始例如。 http://djangopackages.com/grids/g/static-builders/正如@ChrisPratt所示! – Stefano 2012-02-05 23:00:28

回答

24

的Django 1.4現在包括CachedStaticFilesStorage這不正是你需要的(嗯... 幾乎)。

您可以將它與manage.py collectstatic任務一起使用。像往常一樣,所有靜態文件都是從您的應用程序中收集的,但此存儲管理器也會爲每個文件創建一個附加名稱的MD5散列的副本。例如,假設你有一個css/styles.css文件,它也會創建類似css/styles.55e7cbb9ba48.css的東西。

當然,正如您所提到的,問題是您不希望您的視圖和模板始終計算MD5哈希以找出合適的URL來生成。解決方案是緩存。好的,你問了一個沒有緩存的解決方案,對不起,這就是爲什麼我說差不多是。但是真的沒有理由拒絕緩存。 CachedStaticFilesStorage使用名爲staticfiles的特定高速緩存。默認情況下,它將使用您現有的緩存系統,並且瞧!但是,如果您不希望它使用常規緩存,也許是因爲它是分佈式緩存,並且您希望避免網絡查詢開銷以獲取靜態文件名,那麼您可以爲staticfiles設置特定的RAM緩存。這比聽起來容易:檢查出this excellent blog post。以下是它的樣子:

CACHES = { 
    'default': { 
    'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache', 
    'LOCATION': '127.0.0.1:11211', 
    }, 
    'staticfiles': { 
    'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', 
    'LOCATION': 'staticfiles-filehashes' 
    } 
} 
+0

對於未來的讀者,[本文](https:/ /docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#cachedstaticfilesstorage)幫助我快速簡單地實現了這個過程 – Anupam 2017-08-22 09:59:26

1

如何在您的URL中始終擁有一個URL版本的URL參數,並且每當您有主要版本時更改URL參數中的版本。即使在DNS中。因此,如果www.yourwebsite.com加載www.yourwebsite.com/index.html?version=1.0然後在主要版本後瀏覽器應加載www.yourwebsite.com/index.html?version=2.0

我想這是類似於您的解決方案1.而不是跟蹤文件可以跟蹤整個目錄?例如ratehr比/static/style/css?v=2.0你可以做/static-2/style/css或使它甚至粒度爲/static/style/cssv2/

13

我會建議使用類似django-compressor之類的東西。除了自動處理這種類型的東西外,它還會自動組合和縮小文件以實現快速頁面加載。

即使您沒有最終完整地使用它,您也可以檢查他們的代碼以獲得指導來設置類似的東西。它比從簡單的StackOverflow答案中得到的任何東西都要好得多。

+0

爲了記錄,django-compressor使用文件修改時間來達到這個目的,類似於@ssbb答案中的推薦 - https://github.com/django-compressor/django-compressor/blob/develop/compressor/ storage.py – Wtower 2016-02-24 10:25:15

+0

即使您在其他地方手動執行js壓縮,也可以使用此解決方案,方法是使用帶有django的輕量級壓縮器,如帶有WHITESPACE_ONLY設置的Closure Compiler,以便利用靜態/緩存文件命名。 – 2017-11-28 14:22:38

8

我用我自己的templatetag其中添加文件的修改日期,以網址:https://bitbucket.org/ad3w/django-sstatic

+1

有史以來最好的答案,你剛剛救了我的一天(和我生命中的一部分<3)。 簡單,簡單,直接。 – 2015-10-14 10:35:00

+0

而不是自己建立路徑,最好讓django尋找它: from django.contrib.staticfiles.finders import find full_path = find(path) – 2017-09-19 15:16:21

5

是否正在重新發明輪子並創建自己的實施方案?此外,我希望低級代碼(例如nginx)在生產中爲我的靜態文件提供服務,而不是在python應用程序中,甚至在後端服務。還有一件事:我希望鏈接在重新計算後保持不變,因此瀏覽器僅提取新文件。所以here's我的觀點:

template.html:

{% load md5url %} 
<script src="{% md5url "example.js" %}"/> 

掉HTML:

static/example.js?v=5e52bfd3 

settings.py:

STATIC_URL = '/static/' 
STATIC_ROOT = os.path.join(PROJECT_DIR, 'static') 

appname中/ templatetags/md5url.py:

import hashlib 
import threading 
from os import path 
from django import template 
from django.conf import settings 

register = template.Library() 


class UrlCache(object): 
    _md5_sum = {} 
    _lock = threading.Lock() 

    @classmethod 
    def get_md5(cls, file): 
     try: 
      return cls._md5_sum[file] 
     except KeyError: 
      with cls._lock: 
       try: 
        md5 = cls.calc_md5(path.join(settings.STATIC_ROOT, file))[:8] 
        value = '%s%s?v=%s' % (settings.STATIC_URL, file, md5) 
       except IsADirectoryError: 
        value = settings.STATIC_URL + file 
       cls._md5_sum[file] = value 
       return value 

    @classmethod 
    def calc_md5(cls, file_path): 
     with open(file_path, 'rb') as fh: 
      m = hashlib.md5() 
      while True: 
       data = fh.read(8192) 
       if not data: 
        break 
       m.update(data) 
      return m.hexdigest() 


@register.simple_tag 
def md5url(model_object): 
    return UrlCache.get_md5(model_object) 

注意,施加變化的uwsgi應用(具體的處理)應該被重新啓動。

+0

這很好,但是這種方法不能使每個請求都計算md5嗎? – Wtower 2016-02-24 09:40:14

+0

不,結果放到靜態類字段。然後我們嘗試從中獲得:cls._md5_sum [file] – deathangel908 2016-02-24 10:02:35

+0

是的,在我的評論後注意到。謝謝。 – Wtower 2016-02-24 10:14:39

1

@ deathangel908代碼有更新。現在它與S3存儲(以及我認爲的任何其他存儲)運行良好。區別在於使用靜態文件存儲來獲取文件內容。原件在S3上不起作用。

APPNAME/templatetags/md5url.py:

import hashlib 
import threading 
from django import template 
from django.conf import settings 
from django.contrib.staticfiles.storage import staticfiles_storage 

register = template.Library() 


class UrlCache(object): 
    _md5_sum = {} 
    _lock = threading.Lock() 

    @classmethod 
    def get_md5(cls, file): 
     try: 
      return cls._md5_sum[file] 
     except KeyError: 
      with cls._lock: 
       try: 
        md5 = cls.calc_md5(file)[:8] 
        value = '%s%s?v=%s' % (settings.STATIC_URL, file, md5) 
       except OSError: 
        value = settings.STATIC_URL + file 
       cls._md5_sum[file] = value 
       return value 

    @classmethod 
    def calc_md5(cls, file_path): 
     with staticfiles_storage.open(file_path, 'rb') as fh: 
      m = hashlib.md5() 
      while True: 
       data = fh.read(8192) 
       if not data: 
        break 
       m.update(data) 
      return m.hexdigest() 


@register.simple_tag 
def md5url(model_object): 
    return UrlCache.get_md5(model_object) 
2

該方案的主要優點:你沒有修改模板什麼。

這會將構建版本添加到STATIC_URL,然後網絡服務器將使用Rewrite規則將其刪除。

設置。PY

# build version, it's increased with each build 
VERSION_STAMP = __versionstr__.replace(".", "") 
# rewrite static url to contain the number 
STATIC_URL = '%sversion%s/' % (STATIC_URL, VERSION_STAMP) 

所以最終的URL將是例如這樣的:

/static/version010/style.css 

然後Nginx已經規則重寫回/static/style.css

location /static { 
    alias /var/www/website/static/; 
    rewrite ^(.*)/version([\.0-9]+)/(.*)$ $1/$3; 
} 
1

簡單templatetag vstatic創建版本化靜態文件URL擴展Django的行爲:

from django.conf import settings 
from django.contrib.staticfiles.templatetags.staticfiles import static 

@register.simple_tag 
def vstatic(path): 
    url = static(path) 
    static_version = getattr(settings, 'STATIC_VERSION', '') 
    if static_version: 
     url += '?v=' + static_version 
    return url 

如果你想自動STATIC_VERSION設置爲當前的git的承諾哈希,您可以使用下面的代碼片段(Python3代碼必要時進行調整):

import subprocess 


def get_current_commit_hash(): 
    try: 
     return subprocess.check_output(['git', 'rev-parse', '--short', 'HEAD']).strip().decode('utf-8') 
    except: 
     return '' 

settings.py電話get_current_commit_hash(),所以這將是隻計算曾經:

STATIC_VERSION = get_current_commit_hash()