Collectstatic和S3:没有找到更新的文件

时间:2013-08-23 22:54:40

标签: django amazon-web-services amazon-s3

我使用Amazon S3为Django项目存储静态文件,但collectstatic找不到更新的文件 - 只有新文件。

我一直在寻找答案,我的猜测是我的配置不正确。我跟着this blog post来帮助设置所有内容。

我也遇到this question这似乎与我的问题相同,但我已经尝试了所有的解决方案。

我甚至尝试使用this plugin中建议的this question

以下是一些可能有用的信息:

settings.py

...
STATICFILES_FINDERS = (
    'django.contrib.staticfiles.finders.FileSystemFinder',
    'django.contrib.staticfiles.finders.AppDirectoriesFinder',
    'django.contrib.staticfiles.finders.DefaultStorageFinder',
)
...
# S3 Settings
AWS_STORAGE_BUCKET_NAME = os.environ['AWS_STORAGE_BUCKET_NAME']
STATICFILES_STORAGE = 'storages.backends.s3boto.S3BotoStorage'
S3_URL = 'http://%s.s3.amazonaws.com/' % AWS_STORAGE_BUCKET_NAME
STATIC_URL = S3_URL
AWS_PRELOAD_METADATA = False

requirements.txt

...
Django==1.5.1
boto==2.10.0
django-storages==1.1.8
python-dateutil==2.1

EDIT1:

如果这个问题对我自己的情况来说太独特,对大量观众有任何帮助,我道歉。尽管如此 - 这长期以来一直在阻碍我的工作效率,而且我浪费了很多时间寻找解决方案,所以我开始奖励那些可以帮助解决这个问题的人。

EDIT2:

我刚遇到类似的问题。我的时区与我的AWS桶的位置不同。如果默认情况下collectstatic使用时间戳,是否会干扰流程?

由于

5 个答案:

答案 0 :(得分:8)

我想我解决了这个问题。和你一样,我在这个问题上花了这么多时间。我也在你在bitbucket上找到的bug报告。这就是我刚刚完成的事情。

我有

django-storages==1.1.8
Collectfast==0.1.11

这根本不起作用。删除所有内容一次也不起作用。之后,它无法接受修改并拒绝更新任何内容。

问题在于我们的时区。 S3会说它的文件最后修改的时间晚于我们要上传的文件。 django collectstatic将不会尝试复制新的。它将调用文件“未修改”。例如,这是我在修复之前看到的内容:

Collected static files in 0:00:45.292022.
Skipped 407 already synced files.
0 static files copied, 1 unmodified.

我的解决方案是“改变时间到地狱!”。除了我们在这里解决的时区问题,如果我犯了错误并需要回滚怎么办?它将拒绝部署旧的静态文件并使我的网站崩溃。

以下是我对Collectfast https://github.com/FundedByMe/collectfast/pull/11的拉取请求。我仍然留下一面旗帜,所以如果你真的想检查修改时间,你仍然可以做到。在合并之前,只需在https://github.com/sunshineo/collectfast

使用我的代码即可

祝你有愉快的一天!

- 戈登 PS:为此,一直待到凌晨4点40分。我的日子已经被毁了。

答案 1 :(得分:6)

经过几个小时的挖掘,我找到了this bug report

我将我的要求更改为恢复到以前版本的Django存储。

django-storages==1.1.5

答案 2 :(得分:3)

您可能需要考虑在Github上使用antonagestam编写的这个插件: https://github.com/FundedByMe/collectfast

它比较文件的校验和,这是确定文件何时更改的有保证的方法。这是另一个stackoverflow问题的接受答案:Faster alternative to manage.py collectstatic (w/ s3boto storage backend) to sync static files to s3?

答案 3 :(得分:1)

这里有一些很好的答案,但今天我花了一些时间在这上面,所以我想再做一次,以防将来帮助某人。根据其他主题中的建议我确认,对我来说,这确实是由时区差异造成的。我的django时间不正确,但设置为EST,S3设置为GMT。在测试中,我恢复了django-storages 1.1.5,它确实似乎得到了收集静态工作。部分由于个人偏好,我不愿意a)回滚三个版本的django-storage并丢失任何潜在的错误修复或b)改变我项目组件的时区,这基本上归结为一个便利功能(尽管很重要)一)。

我编写了一个简短的脚本来完成与collectstatic相同的工作而没有上述改动。它需要对您的应用进行一些修改,但如果它放在应用程序级别并且'static_dirs'替换为项目应用程序的名称,则应该适用于标准情况。它通过终端运行'python whatever_you_call_it.py -e environment_name(将其设置为你的aws存储桶)。

import sys, os, subprocess
import boto3
import botocore
from boto3.session import Session
import argparse
import os.path, time
from datetime import datetime, timedelta
import pytz

utc = pytz.UTC
DEV_BUCKET_NAME = 'dev-homfield-media-root'
PROD_BUCKET_NAME = 'homfield-media-root'
static_dirs = ['accounts', 'messaging', 'payments', 'search', 'sitewide']

def main():
    try: 
        parser = argparse.ArgumentParser(description='Homfield Collectstatic. Our version of collectstatic to fix django-storages bug.\n')
        parser.add_argument('-e', '--environment', type=str, required=True, help='Name of environment (dev/prod)')
        args = parser.parse_args()
        vargs = vars(args)
        if vargs['environment'] == 'dev':
            selected_bucket = DEV_BUCKET_NAME
            print "\nAre you sure? You're about to push to the DEV bucket. (Y/n)"
        elif vargs['environment'] == 'prod':
            selected_bucket = PROD_BUCKET_NAME
            print "Are you sure? You're about to push to the PROD bucket. (Y/n)"
        else:
            raise ValueError

        acceptable = ['Y', 'y', 'N', 'n']
        confirmation = raw_input().strip()
        while confirmation not in acceptable:
            print "That's an invalid response. (Y/n)"
            confirmation = raw_input().strip()

        if confirmation == 'Y' or confirmation == 'y':
            run(selected_bucket)
        else:
            print "Collectstatic aborted."
    except Exception as e:
        print type(e)
        print "An error occured. S3 staticfiles may not have been updated."


def run(bucket_name):

    #open session with S3
    session = Session(aws_access_key_id='{aws_access_key_id}',
        aws_secret_access_key='{aws_secret_access_key}',
        region_name='us-east-1')
    s3 = session.resource('s3')
    bucket = s3.Bucket(bucket_name)

    # loop through static directories
    for directory in static_dirs:
        rootDir = './' + directory + "/static"
        print('Checking directory: %s' % rootDir)

        #loop through subdirectories
        for dirName, subdirList, fileList in os.walk(rootDir):
            #loop through all files in subdirectory
            for fname in fileList:
                try:
                    if fname == '.DS_Store':
                        continue

                    # find and qualify file last modified time
                    full_path = dirName + "/" + fname
                    last_mod_string = time.ctime(os.path.getmtime(full_path))
                    file_last_mod = datetime.strptime(last_mod_string, "%a %b %d %H:%M:%S %Y") + timedelta(hours=5)
                    file_last_mod = utc.localize(file_last_mod)

                    # truncate path for S3 loop and find object, delete and update if it has been updates
                    s3_path = full_path[full_path.find('static'):]
                    found = False
                    for key in bucket.objects.all():
                        if key.key == s3_path:
                            found = True 
                            last_mode_date = key.last_modified
                            if last_mode_date < file_last_mod:
                                key.delete()
                                s3.Object(bucket_name, s3_path).put(Body=open(full_path, 'r'), ContentType=get_mime_type(full_path))
                                print "\tUpdated : " + full_path
                    if not found:
                        # if file not found in S3 it is new, send it up
                        print "\tFound a new file. Uploading : " + full_path
                        s3.Object(bucket_name, s3_path).put(Body=open(full_path, 'r'), ContentType=get_mime_type(full_path))
                except:
                    print "ALERT: Big time problems with: " + full_path + ". I'm bowin' out dawg, this shitz on u." 


def get_mime_type(full_path):
    try:
        last_index = full_path.rfind('.')
        if last_index < 0:
            return 'application/octet-stream'
        extension = full_path[last_index:]
        return {
            '.js' : 'application/javascript',
            '.css' : 'text/css',
            '.txt' : 'text/plain',
            '.png' : 'image/png',
            '.jpg' : 'image/jpeg',
            '.jpeg' : 'image/jpeg',
            '.eot' : 'application/vnd.ms-fontobject',
            '.svg' : 'image/svg+xml',
            '.ttf' : 'application/octet-stream',
            '.woff' : 'application/x-font-woff',
            '.woff2' : 'application/octet-stream'
        }[extension]
    except:
        'ALERT: Couldn\'t match mime type for '+ full_path + '. Sending to S3 as application/octet-stream.'

if __name__ == '__main__':
    main()

答案 4 :(得分:0)

我有一个类似的问题将新文件推送到S3存储桶(以前运行良好),但是没有关于django或python的问题,在我结束时我修复了当我删除本地存储库并再次克隆它时的问题。 p>