从 django 静态文件提供服务并上传到 heroku 时无法找到反应静态文件

Posted

技术标签:

【中文标题】从 django 静态文件提供服务并上传到 heroku 时无法找到反应静态文件【英文标题】:Unable to locate react static files when serving from django staticfiles and uploading to heroku 【发布时间】:2021-07-30 13:42:59 【问题描述】:

我正在尝试向 Heroku 添加一个 Django 后端和一个反应前端。关注this tutorial。我正在使用白噪声来提供静态文件。

在运行python manage.py collectstatic 时,我不断收到同样的错误:

django.core.exceptions.SuspiciousFileOperation: The joined path (C:\Users\<name>\Documents\combined_connect\static\media\background-pattern.f6347303.png) is located outside of the base path component (C:\Users\<name>\Documents\combined_connect\staticfiles)

这个命令在部署时也由 heroku python hooks 运行。 python钩子在react钩子之后运行,所以react build文件夹肯定在那里。出于测试目的,当我调用collectstatic 命令时,我还在本地运行了build

问题与我如何定义静态根有关,但对于我的生活,我无法弄清楚。我使用了多个教程、静态文件上的 django 文档和静态文件上的 heroku dev 文档。

为什么它在静态中寻找我的媒体文件(background-pattern.png 等)?在collectstatic 上,所有内容都被放入staticfiles 目录中。无论我将文件名或变量更改为什么,关于staticfilesstatic 之间查找冲突的错误总是会发生。

我是这样定义它们的:

settings.py

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
​

TEMPLATES = [
    
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'build')], # pointing to the React build folder
        'APP_DIRS': True,
        'OPTIONS': 
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        ,
    ,
]

STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
STATIC_URL = 'build/static/'
​
STATICFILES_DIRS = (
    os.path.join(BASE_DIR, 'build/static'),
)

项目的结构如下:

编辑:使用这个配置,我可以很好地为后端服务。如果我转到根 url /,我会看到 Django 404 页面而不是我的 React 主页。转到/admin 效果很好,我可以调用/api/ 端点。

【问题讨论】:

这里是和你一样的问题的答案 - ***.com/questions/37241902/… 【参考方案1】:

首先可能需要澄清一下:-)

“媒体”目录用于例如用户上传的内容。所以它更多的是动态内容,而不是静态的。或者至少对于您的服务的每个实例都不是静态的。因此,它通常也不随您的应用程序一起提供。详情在django documentation

同时检查settings documentation about MEDIA_ROOT

我假设在您的设置中您的设置类似于 MEDIA_ROOT = "build/static/media/" 或类似的东西,这会导致冲突。

如果您想使用“媒体”文件,即用户上传的文件,那么无论如何您都需要在 Heroku 上使用不同的存储空间。 Heroku Dynos 有一个ephemeral filesystem,这意味着您每天都会获得一次实例的新副本,这会导致您在实例生命周期内存储的所有数据丢失。

要解决此问题,您可以将这些媒体文件存储在例如 AWS S3 上。 Django 有一个很棒的addon,它将文件系统存储替换为 AWS S3 存储。这意味着从您作为开发人员的角度来看,API 根本不会改变。

这是一个使用 django-s3-storage 的 Django 开发和 PROD 设置示例:

开发示例配置(没什么特别的,没有 S3):

class Development(Configuration):

    ... # Your code

    @property
    def MEDIA_ROOT(self):
        default_value = os.path.join(self.BASE_DIR, 'public', 'media')
        return values.Value(default=default_value, environ_name='MEDIA_ROOT')


    @property
    def STATIC_ROOT(self):
        default_value = os.path.join(self.BASE_DIR, 'static')
        return values.Value(default=default_value, environ_name='STATIC_ROOT')

    @property
    def STATICFILES_DIRS(self):
        return (
            os.path.join(self.BASE_DIR, 'whatever', 'react', 'build'),
            os.path.join(self.BASE_DIR, 'yourapp', 'admin', 'static'),
        )
    ... # Your code

为了您的本地开发,请确保媒体和静态不要指向相同的目录/结构。

生产示例配置:

# Inheriting from Development only serves as example. Good practice would be to move code used by both environments into a `Base` class. 
class Production(Development):
    ... # Your code

    DEFAULT_FILE_STORAGE = 'django_s3_storage.storage.S3Storage'

    AWS_REGION = "us-east-1"

    AWS_ACCESS_KEY_ID = values.Value(default='', environ_name='AWS_ACCESS_KEY_ID')
    AWS_SECRET_ACCESS_KEY = values.Value(default='', environ_name='AWS_SECRET_ACCESS_KEY')
    AWS_S3_BUCKET_NAME = values.Value(default='', environ_name='AWS_S3_BUCKET_NAME')

    @property
    def INSTALLED_APPS(self):
        return super().INSTALLED_APPS + [
            'django_s3_storage',
        ]

    ... # Your code

【讨论】:

感谢您的详细解释。周末才有时间测试,但一定要在赏金到期前测试一下。 不着急...我不在乎赏金。恰好我前段时间使用 Django/React 在 Heroku 上部署了一个项目,并且在媒体/静态文件方面遇到了类似的问题。 最后没有机会对此进行测试,但这是一个非常详细的答案,我相信它对任何在这里找到自己方式的人都非常有用。将其标记为正确,因为其他人也对此表示赞同。

以上是关于从 django 静态文件提供服务并上传到 heroku 时无法找到反应静态文件的主要内容,如果未能解决你的问题,请参考以下文章

仅在我重新加载 django 服务器后才提供上传的媒体文件

如何将文件直接从 Django admin 上传到 Amazon S3?缓解 Heroku 应用程序错误(超时)

将文件从iPhone上传到Django

Django - 从静态文件夹提供 SPA

nginx 从根服务静态文件并从别名上传文件

Django_上传图片和模版获取图片