If you serve Django behind Nginx, then you can delegate the file download service to Nginx and get increased performance:
See Nginx X-accel documentation [1] for details.
As an example, let’s consider an application called “myapp”.
settings.py:
INSTALLED_APPS = (
# ...
'myapp',
# ...
)
MYAPP_STORAGE_LOCATION = '/var/www/files/' # Could be MEDIA_ROOT for public
# files.
This application holds a Document model.
myapp/models.py:
from django.conf import settings
from django.core.files.storage import FileSystemStorage
from django.db import models
storage = FileSystemStorage(location=settings.MYAPP_STORAGE_LOCATION)
class Document(models.Model):
file = models.FileField(storage=storage, upload_to='document')
Notice the storage and upload_to parameters: files for Document model live in /var/www/files/document/ folder.
Then we configured a download view for this model, restricted to authenticated users:
myapp/urls.py:
from django.conf.urls import url, url_patterns
from django.contrib.auth.decorators import login_required
from django_downloadview import ObjectDownloadView
from myapp.models import Document
download = login_required(ObjectDownloadView.as_view(model=Document))
url_patterns = ('',
url('^document/(?P<pk>[0-9]+/download/$', download, name='download'),
)
As is, Django is to serve the files, i.e. load chunks into memory and stream them.
Nginx is much more efficient for the actual streaming... Let’s use it!
See Nginx X-accel documentation [1] for details.
Here is what you could have in /etc/nginx/sites-available/default:
charset utf-8;
# Django-powered service.
upstream frontend {
server 127.0.0.1:8000 fail_timeout=0;
}
server {
listen 80 default;
# File-download proxy.
#
# Will serve /var/www/files/myfile.tar.gz when passed URI
# like /optimized-download/myfile.tar.gz
#
# See http://wiki.nginx.org/X-accel
# and https://github.com/benoitbryon/django-downloadview
location /optimized-download {
internal;
# Location to files on disk.
# See Django's settings.NGINX_DOWNLOAD_MIDDLEWARE_MEDIA_ROOT
alias /var/www/files/;
}
# Proxy to Django-powered frontend.
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://frontend;
}
}
... where specific configuration is the location /optimized-download section.
Note
/optimized-download is not available for the client, i.e. users won’t be able to download files via /optimized-download/<filename>.
Warning
Make sure Nginx can read the files to download! Check permissions.
If you want to delegate all file downloads to Nginx, then use django_downloadview.nginx.XAccelRedirectMiddleware.
Register it in your settings:
MIDDLEWARE_CLASSES = (
# ...
'django_downloadview.nginx.XAccelRedirectMiddleware',
# ...
)
Setup the middleware:
NGINX_DOWNLOAD_MIDDLEWARE_MEDIA_ROOT = MYAPP_STORAGE_LOCATION
NGINX_DOWNLOAD_MIDDLEWARE_MEDIA_URL = '/optimized-download'
Optionally fine-tune the middleware. Default values are None, which means “use Nginx’s defaults”.
NGINX_DOWNLOAD_MIDDLEWARE_EXPIRES = False # Force no expiration.
NGINX_DOWNLOAD_MIDDLEWARE_WITH_BUFFERING = False # Force buffering off.
NGINX_DOWNLOAD_MIDDLEWARE_LIMIT_RATE = False # Force limit rate off.
If you want to delegate file downloads to Nginx on a per-view basis, then use django_downloadview.nginx.x_accel_redirect() decorator.
Adapt myapp/urls.py:
from django.conf.urls import url, url_patterns
from django.contrib.auth.decorators import login_required
from django_downloadview import ObjectDownloadView
+ from django_downloadview.nginx import x_accel_redirect
from myapp.models import Document
download = login_required(ObjectDownloadView.as_view(model=Document))
+ download = x_accel_redirect(download,
+ media_root=settings.MY_APP_STORAGE_LOCATION,
+ media_url='/optimized-download')
url_patterns = ('',
url('^document/(?P<pk>[0-9]+/download/$', download, name='download'),
)
Add charset utf-8; in your nginx configuration file.
Check your settings.NGINX_DOWNLOAD_MIDDLEWARE_MEDIA_ROOT in Django configuration VS alias in nginx configuration: in a standard configuration, they should be equal.
[1] | (1, 2) http://wiki.nginx.org/X-accel |