使用Django提供受保护的媒体文件

pinkon5k  于 2023-05-19  发布在  Go
关注(0)|答案(3)|浏览(160)

我希望Django提供一些媒体文件(例如。用户上传的文件)仅适用于登录用户。由于我的网站流量很低,我想我会保持简单,不要使用django-sendfile来告诉Nginx什么时候提供文件。相反,我会让Django/Gunicorn来做这项工作。对我来说,这似乎简单得多,对于一个低流量的网站,这可能更安全。
但是组织文件存储位置的最佳方法是什么?媒体文件都存储在MEDIA_ROOT下面,这个目录由Nginx在生产中提供。如果我上传我的文件到MEDIA_ROOT/protected/,我必须告诉Nginx不要提供子目录protected中的文件。
但这是个好主意吗?首先允许Nginx访问/media/,然后保护子目录/media/protected/,对我来说似乎有点风险。不使用MEDIA_ROOT的子目录来存储受保护的文件不是更好吗?
但如果我在模型中尝试这样的快速和肮脏的东西:

upload_to='../protected/documents/%Y/%m/'

Django抱怨道:

SuspiciousFileOperation at /admin/core/document/add/
The joined path (/home/me/projects/project/protected/documents/2016/09/test.file) is located outside of the base path component (/home/me/projects/project/media)

所以我认为“离开”MEDIA_ROOT不是一个好的做法。
存储和提供受保护媒体文件的最佳解决方案是什么?

cgvd09ve

cgvd09ve1#

直接从视图提供媒体文件(可能是大文件)是不好的。你可以使用nginx服务器上提供的sendfile扩展;示例Nginx配置如下。

location /projects/project/media/{
    # this path is not public
    internal;
    # absolute path
    alias /projects/project/media/;
 }

将视图更改为

@login_required
def serve_protected_document(request, file):
    document = get_object_or_404(ProtectedDocument, file="protected/documents/" + file)

    # Split the elements of the path
    path, file_name = os.path.split(file)

    response = HttpResponse()
    response["Content-Disposition"] = "attachment; filename=" + file_name
    # nginx uses this path to serve the file
    response["X-Accel-Redirect"] = document.name # path to file
    return response

链接:在nginx上配置sendfile扩展名的更多细节是here

xdnvmnnf

xdnvmnnf2#

我现在提出了以下解决方案:
在我的Django设置中:

MEDIA_ROOT = "/projects/project/media/"
MEDIA_URL = "/media/

在我的模型中,我可以:

document = models.FileField(upload_to="public/documents")

document = models.FileField(upload_to="protected/documents")

这样,我现在有两个子目录'public'和'protected'在我的媒体文件目录。
Nginx或Djangos开发服务器只提供'public'子目录中的文件。
对于Djangos开发服务器:

if os.environ["ENVIRONMENT_TYPE"] == 'development':
    urlpatterns += static(settings.MEDIA_URL + "public/", document_root=settings.MEDIA_ROOT + "public/")

对于Nginx(用于生产):

location /media/public/ {
    alias   /projects/project/media/public/;
}

当我想提供受保护的文档时,我会执行以下操作:
在urls.py:

url(r'^media/protected/documents/(?P<file>.*)$', core.views.serve_protected_document, name='serve_protected_document'),

在views.py:

@login_required()
def serve_protected_document(request, file):
    document = get_object_or_404(ProtectedDocument, file="protected/documents/" + file)

    # Split the elements of the path
    path, file_name = os.path.split(file)

    response = FileResponse(document.file,)
    response["Content-Disposition"] = "attachment; filename=" + file_name

    return response

我会很感激任何评论!有没有更好的方法来实现这一点?

vd2z7a6w

vd2z7a6w3#

project/urls.py

from django.conf import settings
from django.conf.urls.static import static
from django.contrib import admin
from django.urls import include, path
from .views import media_protect
urlpatterns = [
...
path('media/protect/<str:app>/<int:author>/<str:file>', media_protect)
...
]

urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)

project/views.py

from django.contrib.auth.decorators import login_required
from django.http import FileResponse
from project import settings

@login_required
def media_protect(request, app, author, file):
    media_root = os.path.join(settings.MEDIA_ROOT)
    if ...:
        response = FileResponse(
            open(f'{media_root}/protect/{app}/{author}/{file}', 'rb'))
        return response
    else ...:
        retun ...

相关问题