mirror of
https://github.com/jazzband/django-downloadview.git
synced 2026-03-16 22:40:25 +00:00
Refs #1 - Introduced experimental support for Nginx X-Accel. WORK IN PROGRESS.
This commit is contained in:
parent
46542cdc3c
commit
41e00d312d
4 changed files with 228 additions and 0 deletions
101
django_downloadview/nginx.py
Normal file
101
django_downloadview/nginx.py
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
"""Let Nginx serve files for increased performance.
|
||||
|
||||
See `Nginx X-accel documentation <http://wiki.nginx.org/X-accel>`_.
|
||||
|
||||
"""
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from django.conf import settings
|
||||
from django.http import HttpResponse
|
||||
|
||||
from django_downloadview.middlewares import BaseDownloadMiddleware
|
||||
from django_downloadview.decorators import DownloadDecorator
|
||||
|
||||
|
||||
#: Default value for X-Accel-Buffering header.
|
||||
DEFAULT_BUFFERING = None
|
||||
if not hasattr(settings, 'NGINX_DOWNLOAD_MIDDLEWARE_BUFFERING'):
|
||||
setattr(settings, 'NGINX_DOWNLOAD_MIDDLEWARE_BUFFERING', DEFAULT_BUFFERING)
|
||||
|
||||
|
||||
#: Default value for X-Accel-Limit-Rate header.
|
||||
DEFAULT_LIMIT_RATE = None
|
||||
if not hasattr(settings, 'NGINX_DOWNLOAD_MIDDLEWARE_LIMIT_RATE'):
|
||||
setattr(settings, 'NGINX_DOWNLOAD_MIDDLEWARE_LIMIT', DEFAULT_LIMIT_RATE)
|
||||
|
||||
|
||||
def content_type_to_charset(content_type):
|
||||
return 'utf-8'
|
||||
|
||||
|
||||
class XAccelRedirectResponse(HttpResponse):
|
||||
"""Http response that delegate serving file to Nginx."""
|
||||
def __init__(self, url, content_type, basename=None, expires=None,
|
||||
with_buffering=None, limit_rate=None):
|
||||
"""Return a HttpResponse with headers for Nginx X-Accel-Redirect."""
|
||||
super(XAccelRedirectResponse, self).__init__(content_type=content_type)
|
||||
basename = basename or url.split('/')[-1]
|
||||
self['Content-Disposition'] = 'attachment; filename=%s' % basename
|
||||
self['X-Accel-Redirect'] = url
|
||||
self['X-Accel-Charset'] = content_type_to_charset(content_type)
|
||||
if with_buffering is not None:
|
||||
self['X-Accel-Buffering'] = with_buffering and 'yes' or 'no'
|
||||
if expires:
|
||||
expire_seconds = timedelta(expires - datetime.now()).seconds
|
||||
self['X-Accel-Expires'] = expire_seconds
|
||||
elif expires is not None: # We explicitely want it off.
|
||||
self['X-Accel-Expires'] = 'off'
|
||||
if limit_rate is not None:
|
||||
self['X-Accel-Limit-Rate'] = limit_rate and '%d' % limit_rate \
|
||||
or 'off'
|
||||
|
||||
|
||||
class BaseXAccelRedirectMiddleware(BaseDownloadMiddleware):
|
||||
"""Looks like a middleware, but configurable."""
|
||||
def __init__(self, expires=None, with_buffering=None, limit_rate=None):
|
||||
"""Constructor."""
|
||||
self.expires = expires
|
||||
self.with_buffering = with_buffering
|
||||
self.limit_rate = limit_rate
|
||||
|
||||
def file_to_url(response):
|
||||
return response.filename
|
||||
|
||||
def process_download_response(self, request, response):
|
||||
"""Replace DownloadResponse instances by NginxDownloadResponse ones."""
|
||||
url = self.file_to_url(response)
|
||||
if self.expires:
|
||||
expires = self.expires
|
||||
else:
|
||||
try:
|
||||
expires = response.expires
|
||||
except AttributeError:
|
||||
expires = None
|
||||
return XAccelRedirectResponse(url=url,
|
||||
content_type=response.content_type,
|
||||
basename=response.basename,
|
||||
expires=expires,
|
||||
with_buffering=self.with_buffering,
|
||||
limit_rate=self.limit_rate)
|
||||
|
||||
|
||||
class XAccelRedirectMiddleware():
|
||||
"""Apply X-Accel-Redirect globally.
|
||||
|
||||
XAccelRedirectResponseHandler with django settings.
|
||||
|
||||
"""
|
||||
def __init__(self):
|
||||
"""Use Django settings as configuration."""
|
||||
super(XAccelRedirectMiddleware, self).__init__(
|
||||
expires=settings.NGINX_DOWNLOAD_MIDDLEWARE_EXPIRESS,
|
||||
with_buffering=settings.NGINX_DOWNLOAD_MIDDLEWARE_WITH_BUFFERING,
|
||||
limit_rate=settings.NGINX_DOWNLOAD_MIDDLEWARE_LIMIT_RATE)
|
||||
|
||||
|
||||
#: Apply BaseXAccelRedirectMiddleware to ``view_func`` response.
|
||||
#:
|
||||
#: Proxies additional arguments (``*args``, ``**kwargs``) to
|
||||
#: :py:meth:`django_downloadview.nginx.BaseXAccelRedirectMiddleware.__init__`:
|
||||
#: ``expires``, ``with_buffering``, and ``limit_rate``.
|
||||
x_accel_redirect = DownloadDecorator(BaseXAccelRedirectMiddleware)
|
||||
|
|
@ -25,6 +25,14 @@ django_downloadview Package
|
|||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`nginx` Module
|
||||
-------------------
|
||||
|
||||
.. automodule:: django_downloadview.nginx
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`response` Module
|
||||
----------------------
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ Contents
|
|||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
nginx
|
||||
api/modules
|
||||
|
||||
|
||||
|
|
|
|||
118
docs/nginx.txt
Normal file
118
docs/nginx.txt
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
###################
|
||||
Nginx optimisations
|
||||
###################
|
||||
|
||||
If you serve Django behind Nginx, then you can delegate the file download
|
||||
service to Nginx and get increased performance:
|
||||
|
||||
* lower resources used by Python/Django workers ;
|
||||
* faster download.
|
||||
|
||||
See `Nginx X-accel documentation`_ for details.
|
||||
|
||||
|
||||
****************************
|
||||
Configure some download view
|
||||
****************************
|
||||
|
||||
As an example, let's consider the following download view:
|
||||
|
||||
* mapped on ``/document/<object-slug>/download``
|
||||
* returns DownloadResponse corresponding to Document's model FileField
|
||||
* Document storage root is :file:`/var/www/files/`.
|
||||
|
||||
Configure Document storage:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
storage = FileSystemStorage(location='var/www/files',
|
||||
url='/optimized-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.
|
||||
|
||||
|
||||
***************
|
||||
Configure Nginx
|
||||
***************
|
||||
|
||||
See `Nginx X-accel documentation`_ for details.
|
||||
|
||||
In this documentation, let's suppose we have something like this:
|
||||
|
||||
.. code-block:: nginx
|
||||
|
||||
# Will serve /var/www/files/myfile.tar.gz
|
||||
# When passed URI /protected_files/myfile.tar.gz
|
||||
location /optimized-download {
|
||||
internal;
|
||||
alias /var/www/files;
|
||||
}
|
||||
|
||||
.. 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.
|
||||
|
||||
|
||||
************************************************
|
||||
Global delegation, with XAccelRedirectMiddleware
|
||||
************************************************
|
||||
|
||||
If you want to delegate all file downloads to Nginx, then use
|
||||
:py:class:`django_downloadview.nginx.XAccelRedirectMiddleware`.
|
||||
|
||||
Register it in your settings:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
MIDDLEWARE_CLASSES = (
|
||||
# ...
|
||||
'django_downloadview.nginx.XAccelRedirectMiddleware',
|
||||
# ...
|
||||
)
|
||||
|
||||
Optionally customize configuration (default is "use Nginx's defaults").
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
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.
|
||||
|
||||
|
||||
*************************************************
|
||||
Local delegation, with x_accel_redirect decorator
|
||||
*************************************************
|
||||
|
||||
If you want to delegate file downloads to Nginx on a per-view basis, then use
|
||||
:py:func:`django_downloadview.nginx.x_accel_redirect` decorator.
|
||||
|
||||
In some urls.py:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# ... import Document and django.core.urls
|
||||
|
||||
from django_downloadview import ObjectDownloadView
|
||||
from django_downloadview.nginx import x_accel_redirect
|
||||
|
||||
|
||||
download = x_accel_redirect(ObjectDownloadView.as_view(model=Document))
|
||||
|
||||
# ... URL patterns using ``download``
|
||||
|
||||
|
||||
**********
|
||||
References
|
||||
**********
|
||||
|
||||
.. target-notes::
|
||||
|
||||
.. _`Nginx X-accel documentation`: http://wiki.nginx.org/X-accel
|
||||
Loading…
Reference in a new issue