Serve protected media files with django
Solution 1
I now came up with the following solution:
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")
or
document = models.FileField(upload_to="protected/documents")
This way, I now have the two subdirectories 'public' and 'protected' in my media files directory.
Nginx or Djangos development server only serves the files in the 'public' subdirectory.
For Djangos development server:
if os.environ["ENVIRONMENT_TYPE"] == 'development':
urlpatterns += static(settings.MEDIA_URL + "public/", document_root=settings.MEDIA_ROOT + "public/")
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:
In urls.py:
url(r'^media/protected/documents/(?P<file>.*)$', core.views.serve_protected_document, name='serve_protected_document'),
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?
Solution 2
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.
location /projects/project/media/{
# this path is not public
internal;
# absolute path
alias /projects/project/media/;
}
change your view to
@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
Link: More details on configuring sendfile extension on nginx is here
user2496550
Updated on July 28, 2022Comments
-
user2496550 almost 2 years
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 toMEDIA_ROOT/protected/
I have to tell Nginx not to serve the files in the subdirectoryprotected
.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 ofMEDIA_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?
-
texnic over 7 yearsI am looking for a solution to the same problem, except that I don't have any public media and that I wouldn't like misusing Django to serve files. But for the moment just one remark. Shouldn't file be opened before using File response? docs.djangoproject.com/en/1.10/ref/request-response/…
-
Renjith Thankachan about 7 years@texnic check my answer
-
tread about 6 yearsThis isn't answering the question. It is about how you show the link on the form edit page.
-
shanemgrey about 6 yearsThe OP specified that the use case did not demand performance, and instead was looking for a simple implementation using the auth functions already in Django.
-
CyberHavenProgramming over 4 yearsYour views.py is not safe. If someone copy/pastes the url from source html, anyone should be able to download the file as long as they are logged in as anyone who has an account.
-
Renjith Thankachan over 4 years@surfer190 Spend some time on how a webserver handle requests compared to a WSGI application, then you will get an idea!