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

时间:2018-05-17 09:10:27

标签: python django amazon-s3 deployment django-storage

我在Heroku上运行了一个Django应用程序。要存储和提供我的静态文件,我将django-storages与我的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

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

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

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

  2. 清单文件在collectstatic部署期间写入,因此,如果任何已运行的服务器应用程序版本的实例在之前从S3读取清单文件部署结束,新的slug接管,他们可以获取错误的静态文件 - 只应该为新slug的实例提供。

    请注意,特别是在Heroku上,新的应用程序实例可以动态弹出,因此即使应用程序确实缓存了清单文件,它的首次提取也可能是部署新的slu ..

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

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

    有可能吗?

3 个答案:

答案 0 :(得分:5)

前段时间我读了this article,我认为这很适合你的情况 在最后一段中存在以下内容:

  

staticfiles.json位于哪里?

     

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

     

因此,ManifestStaticFilesStorage将寻找   staticfiles.json中的STATIC_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
     

通过上述更改,Django静态模板标签现在将读取   来自staticfiles.json的映射,它位于项目根目录中。

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

答案 1 :(得分:3)

有Django票证#27590解决了这个问题。该票证具有实现解决方案的拉取请求,但尚未进行审核。

答案 2 :(得分:1)

@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))

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

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