Refs #3 - Introduced DownloadResponse.

This commit is contained in:
Benoit Bryon 2012-11-16 23:29:55 +01:00
parent 0ae3653d1e
commit 5a432dc700
2 changed files with 67 additions and 10 deletions

View file

@ -0,0 +1,52 @@
"""HttpResponse subclasses."""
from django.http import HttpResponse
class DownloadResponse(HttpResponse):
"""File download response."""
def __init__(self, content, content_type, content_length, basename,
status=200, content_encoding=None, expires=None,
filename=None):
"""Constructor.
It differs a bit from HttpResponse constructor.
Required arguments:
* ``content`` is supposed to be an iterable that can read the file.
Consider :py:class:`wsgiref.util.FileWrapper`` as a good candidate.
* ``content_type`` contains mime-type and charset of the file.
It is used as "Content-Type" header.
* ``content_length`` is the size, in bytes, of the file.
It is used as "Content-Length" header.
* ``basename`` is the client-side name of the file ("save as" name).
It is used in "Content-Disposition" header.
Optional arguments:
* ``status`` is HTTP status code.
* ``content_encoding`` is used for "Content-Encoding" header.
* ``expires`` is a datetime.
It is used to set the "Expires" header.
* ``filename`` is the server-side name of the file.
It may be used by decorators or middlewares to delegate the actual
streaming to a more efficient server (i.e. Nginx, Lighttpd...).
"""
super(DownloadResponse, self).__init__(content=content, status=status,
content_type=content_type)
self.filename = filename
self.basename = basename
self['Content-Length'] = content_length
if content_encoding:
self['Content-Encoding'] = content_encoding
self.expires = expires
if expires:
self['Expires'] = expires
self['Content-Disposition'] = 'attachment; filename=%s' % basename

View file

@ -6,11 +6,13 @@ from wsgiref.util import FileWrapper
from django.conf import settings
from django.core.files import File
from django.core.files.storage import DefaultStorage
from django.http import Http404, HttpResponse, HttpResponseNotModified
from django.http import Http404, HttpResponseNotModified
from django.views.generic.base import View
from django.views.generic.detail import BaseDetailView
from django.views.static import was_modified_since
from django_downloadview.response import DownloadResponse
class DownloadMixin(object):
"""Placeholders and base implementation to create file download views.
@ -27,7 +29,7 @@ class DownloadMixin(object):
"""
#: Response class to be used in render_to_response().
response_class = HttpResponse
response_class = DownloadResponse
def get_file(self):
"""Return a django.core.files.File object, which is to be served."""
@ -130,13 +132,16 @@ class DownloadMixin(object):
basename = self.get_basename()
encoding = self.get_encoding()
wrapper = self.get_file_wrapper()
response = self.response_class(wrapper, content_type=content_type)
if encoding:
response['Content-Encoding'] = encoding
response['Content-Length'] = size
# Do not call fsock.close() as HttpResponse needs it open.
# Garbage collector will close it.
response['Content-Disposition'] = 'attachment; filename=%s' % basename
response = self.response_class(content=wrapper,
content_type=content_type,
content_length=size,
filename=filename,
basename=basename,
content_encoding=encoding,
expires=None)
# Do not close the file as response class may need it open: the wrapper
# is an iterator on the content of the file.
# Garbage collector will close the file.
return response
@ -172,7 +177,7 @@ class DownloadView(DownloadMixin, View):
self._file = File(open(self.filename))
return self._file
except IOError:
raise Http404
raise Http404()
def get(self, request, *args, **kwargs):
"""Handle GET requests: stream a file."""