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.
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
.
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 complains:
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)
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?
Renjith Thankachan :
Serving media files ( that may be large files) from view directly is not good. You can use sendfile extension available in nginx server; a sample nginx configuration is like below.\n\n location /projects/project/media/{\n # this path is not public\n internal;\n # absolute path\n alias /projects/project/media/;\n }\n\n\nchange your view to\n\n@login_required\ndef serve_protected_document(request, file):\n document = get_object_or_404(ProtectedDocument, file=\"protected/documents/\" + file)\n\n # Split the elements of the path\n path, file_name = os.path.split(file)\n\n response = HttpResponse()\n response[\"Content-Disposition\"] = \"attachment; filename=\" + file_name\n # nginx uses this path to serve the file\n response[\"X-Accel-Redirect\"] = document.name # path to file\n return response\n\n\nLink: More details on configuring sendfile extension on nginx is here ",
2017-04-05T06:31:24
user2496550 :
I now came up with the following solution:\n\nI have this in my Django settings:\n\nMEDIA_ROOT = \"/projects/project/media/\"\nMEDIA_URL = \"/media/\n\n\nIn my models I do either:\n\ndocument = models.FileField(upload_to=\"public/documents\")\n\n\nor\n\ndocument = models.FileField(upload_to=\"protected/documents\")\n\n\nThis way, I now have the two subdirectories 'public' and 'protected' in my media files directory.\n\nNginx or Djangos development server only serves the files in the 'public' subdirectory.\n\nFor Djangos development server:\n\nif os.environ[\"ENVIRONMENT_TYPE\"] == 'development':\n urlpatterns += static(settings.MEDIA_URL + \"public/\", document_root=settings.MEDIA_ROOT + \"public/\")\n\n\nAnd for Nginx (used in production):\n\nlocation /media/public/ {\n alias /projects/project/media/public/;\n}\n\n\nWhen I want to serve a protected document, I do the following:\n\nIn urls.py:\n\nurl(r'^media/protected/documents/(?P<file>.*)$', core.views.serve_protected_document, name='serve_protected_document'),\n\n\nAnd in views.py:\n\n@login_required()\ndef serve_protected_document(request, file):\n document = get_object_or_404(ProtectedDocument, file=\"protected/documents/\" + file)\n\n # Split the elements of the path\n path, file_name = os.path.split(file)\n\n response = FileResponse(document.file,)\n response[\"Content-Disposition\"] = \"attachment; filename=\" + file_name\n\n return response\n\n\nI would appreciate any comments! Are there better ways to implement this?",
2016-09-28T13:38:30