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

Posted

技术标签:

【中文标题】使用 django 提供受保护的媒体文件【英文标题】:Serve protected media files with django 【发布时间】:2017-02-06 06:16:42 【问题描述】:

我希望 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 不是一个好习惯。

存储和提供受保护媒体文件的最佳解决方案是什么?

【问题讨论】:

django-downloadview 【参考方案1】:

直接从视图中提供媒体文件(可能是大文件)并不好。您可以使用 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

【讨论】:

这不是在回答问题。这是关于如何在表单编辑页面上显示链接。 OP 指定用例不需要性能,而是使用 Django 中已有的 auth 函数寻找一个简单的实现。 @surfer190 与 WSGI 应用程序相比,花点时间了解一下网络服务器如何处理请求,然后你就会明白了!【参考方案2】:

我现在想出了以下解决方案:

我的 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' 子目录中的文件。

对于 Django 的开发服务器:

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

如果有任何 cmets,我将不胜感激!有没有更好的方法来实现这一点?

【讨论】:

我正在寻找相同问题的解决方案,除了我没有任何公共媒体并且我不希望滥用 Django 来提供文件。但目前只有一句话。在使用文件响应之前不应该打开文件吗? docs.djangoproject.com/en/1.10/ref/request-response/… @texnic 检查我的答案 您的 views.py 不安全。如果有人从源 html 复制/粘贴 url,任何人都应该能够下载该文件,只要他们以任何拥有帐户的人身份登录。

以上是关于使用 django 提供受保护的媒体文件的主要内容,如果未能解决你的问题,请参考以下文章

如何读写受密码保护的excel文件?

ASP.NET 从受密码保护的网络共享中读取文件

受信任的证书条目不受密码保护 java

受信任的证书条目不受密码保护 java

如何在java中打开受密码保护的docx文件?

使用 OpenXml 读取受密码保护的 Excel 文件