将静态文件存储在S3上,但staticfiles.json在本地显示

Dan*_*nra 11 python deployment django amazon-s3 django-storage

我有一个在Heroku上运行的Django应用程序.为了存储和提供我的静态文件,我将django-storage与我的S3存储桶以及标准的Django一起使用ManifestFilesMixin.我也在使用django-pipeline.

在代码中:

from django.contrib.staticfiles.storage import ManifestFilesMixin
from storages.backends.s3boto import S3BotoStorage
from pipeline.storage import PipelineMixin

class S3PipelineManifestStorage(PipelineMixin, ManifestFilesMixin, S3BotoStorage):
    pass
Run Code Online (Sandbox Code Playgroud)

设置有效,但staticfiles.json清单存储在S3上.我可以看到两个问题:

  1. 我的应用程序的存储实例必须staticfiles.json从S3 获取,而不是仅从本地文件系统获取它.这在性能方面毫无意义.清单文件的唯一使用者是服务器应用程序本身,因此它也可以存储在本地文件系统而不是远程.

    我不确定这个问题有多重要,因为我想(或希望)服务器应用程序在读取一次后缓存该文件.

  2. 清单文件是部署期间编写的collectstatic,因此,如果任何已运行的服务器应用程序版本的实例在部署完成之前从S3读取清单文件并且新的slug接管,则它们可能会获取错误的静态文件 -只应该为新的slug实例提供服务.

    请注意,特别是在Heroku上,新的应用程序实例可能会动态弹出,因此即使应用程序确实缓存了清单文件,它的第一次获取也可能是在部署新的slug期间.

    所描述的场景特定于Heroku,但我想其他环境会出现类似的问题.

显而易见的解决方案是将清单文件存储在本地文件系统上.每个slug都有自己的清单文件,性能最佳,并且不会有如上所述的任何部署比赛.

可能吗?

Joh*_*fis 6

前段时间我读了这篇文章,我认为这篇文章很适合你.
在最后一段中存在以下内容:

staticfiles.json在哪里?

默认情况下staticfiles.json将驻留在STATIC_ROOT收集所有静态文件的目录中.
我们将所有静态资产托管在S3存储桶上,这意味着staticfiles.json默认情况下最终会同步到S3.但是,我们希望它存在于代码目录中,以便我们可以打包并将其发送到每个应用服务器.

由于这一结果,ManifestStaticFilesStorage将寻找 staticfiles.jsonSTATIC_ROOT以阅读的映射.我们不得不覆盖这种行为,所以我们将子类化ManifestStaticFilesStorage:

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

class KoganManifestStaticFilesStorage(ManifestStaticFilesStorage):

    def read_manifest(self):
        """
        Looks up staticfiles.json in Project directory
        """
        manifest_location = os.path.abspath(
            os.path.join(settings.PROJECT_ROOT, self.manifest_name)
        )
        try:
            with open(manifest_location) as manifest:
                return manifest.read().decode('utf-8')
        except IOError:
            return None
Run Code Online (Sandbox Code Playgroud)

通过上述更改,Django静态模板标记现在将读取staticfiles.json项目根目录中驻留的映射.

没有自己使用它,所以如果有帮助请告诉我!


dir*_*ten 5

@John 和Kogan的回答很好,但没有提供完成这项工作所需的完整代码:正如@Danra 提到的,您还需要将 保存staticfiles.json在源文件夹中才能完成这项工作。这是我根据上述答案创建的代码:

import json
import os
from django.conf import settings
from django.core.files.base import ContentFile
from django.core.files.storage import FileSystemStorage

from whitenoise.storage import CompressedManifestStaticFilesStorage
# or if you don't use WhiteNoiseMiddlware:
# from django.contrib.staticfiles.storage import ManifestStaticFilesStorage


class LocalManifestStaticFilesStorage(CompressedManifestStaticFilesStorage):
    """
    Saves and looks up staticfiles.json in Project directory
    """
    manifest_location = os.path.abspath(settings.BASE_DIR)  # or settings.PROJECT_ROOT depending on how you've set it up in your settings file.
    manifest_storage = FileSystemStorage(location=manifest_location)

    def read_manifest(self):

        try:
            with self.manifest_storage.open(self.manifest_name) as manifest:
                return manifest.read().decode('utf-8')
        except IOError:
            return None

    def save_manifest(self):
        payload = {'paths': self.hashed_files, 'version': self.manifest_version}
        if self.manifest_storage.exists(self.manifest_name):
            self.manifest_storage.delete(self.manifest_name)
        contents = json.dumps(payload).encode('utf-8')
        self.manifest_storage._save(self.manifest_name, ContentFile(contents))
Run Code Online (Sandbox Code Playgroud)

现在你可以使用LocalManifestStaticFilesStorage你的STATICFILES_STORAGE. 运行时manage.py collectstatic,您的清单将保存到您的根项目文件夹中,Django 将在提供内容时在那里寻找它。

如果您的部署包含多个虚拟机,请确保collectstatic只运行一次并将staticfiles.json文件复制到部署中的所有计算机作为代码部署的一部分。这样做的好处是,即使有些机器还没有最新的更新,它们仍然会提供正确的内容(对应于当前版本的代码),因此您可以在有问题的地方执行逐步部署混合状态。