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

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

问题描述:

我希望 Django 只为登录用户提供一些媒体文件(例如用户上传的文件).由于我的网站流量非常低,我想我会保持简单并且不使用 django-sendfile 来告诉 Nginx 何时提供文件.相反,我会让 Django/Gunicorn 来完成这项工作.对我来说,这似乎更简单,对于低流量网站,这可能更安全.

I'd like Django to serve some media files (e.g. user-uploaded files) only for logged-in users. Since my site is quite low-traffic, I think I will keep things simple and do not use django-sendfile to tell Nginx when to serve a file. Instead I'll let Django/Gunicorn do the job. To me this seems a lot simpler and for a low traffic site this maybe more secure.

但是组织文件存储位置的最佳方式是什么?媒体文件都存储在 MEDIA_ROOT 下,这个目录由 Nginx 在生产中提供.如果我将文件上传到 MEDIA_ROOT/protected/,我必须告诉 Nginx 不要提供子目录 protected 中的文件.

But what is the best way to organize the file storage location? Media files are all stored below MEDIA_ROOT and this directory is served by Nginx in production. If I upload my files to MEDIA_ROOT/protected/ I have to tell Nginx not to serve the files in the subdirectory protected.

但这是个好主意吗?在我看来,首先允许 Nginx 访问 /media/ 然后保护子目录 /media/protected/ 似乎有点冒险.不使用MEDIA_ROOT 的子目录来存储受保护的文件不是更好吗?

But is this a good idea? It seems a litte risky to me to allow Nginx access /media/ in the first place and then protect the subdirectory /media/protected/. Wouldn't it be better not to use a subdirectory of MEDIA_ROOT to store protected files?

但是如果我在我的模型中尝试像这样快速而肮脏的东西:

But if I try something like this quick-and-dirty in my model:

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 不是一个好习惯.

So I thing it is not good practice to "leave" the MEDIA_ROOT.

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

What is the best solution to store and serve protected media files?

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

I now came up with the following solution:

我的 Django 设置中有这个:

I have this in my Django settings:

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

在我的模型中,我会这样做:

In my models I do either:

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

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

这样,我的媒体文件目录中现在有两个子目录public"和protected".

This way, I now have the two subdirectories 'public' and 'protected' in my media files directory.

Nginx 或 Djangos 开发服务器只提供 'public' 子目录中的文件.

Nginx or Djangos development server only serves the files in the 'public' subdirectory.

对于 Django 开发服务器:

For Djangos development server:

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

对于 Nginx(用于生产):

And for Nginx (used in production):

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

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

When I want to serve a protected document, I do the following:

在 urls.py 中:

In urls.py:

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

在 views.py 中:

And in 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

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

I would appreciate any comments! Are there better ways to implement this?