mirror of
https://github.com/jazzband/django-downloadview.git
synced 2026-05-21 22:01:58 +00:00
Refactored generic download views. Added storage argument to DownloadView. Renamed DownloadView's 'file' argument to 'filename'.
This commit is contained in:
parent
5a2a174b3e
commit
33ad5d492e
3 changed files with 106 additions and 20 deletions
|
|
@ -71,7 +71,7 @@ class DownloadViewTestCase(DownloadTestCase):
|
|||
"""Download_hello_world view returns hello-world.txt as attachement."""
|
||||
response = self.client.get(self.download_hello_world_url)
|
||||
self.assertEquals(response.status_code, 200)
|
||||
self.assertEquals(response['Content-Type'], 'application/octet-stream')
|
||||
self.assertEquals(response['Content-Type'], 'text/plain')
|
||||
self.assertEquals(response['Content-Disposition'],
|
||||
'attachment; filename=hello-world.txt')
|
||||
self.assertEqual(open(self.files['hello-world.txt']).read(),
|
||||
|
|
@ -89,7 +89,7 @@ class ObjectDownloadViewTestCase(DownloadTestCase):
|
|||
)
|
||||
response = self.client.get(self.download_document_url)
|
||||
self.assertEquals(response.status_code, 200)
|
||||
self.assertEquals(response['Content-Type'], 'application/octet-stream')
|
||||
self.assertEquals(response['Content-Type'], 'text/plain')
|
||||
self.assertEquals(response['Content-Disposition'],
|
||||
'attachment; filename=hello-world.txt')
|
||||
self.assertEqual(open(self.files['hello-world.txt']).read(),
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ fixtures_dir = join(app_dir, 'fixtures')
|
|||
hello_world_file = join(fixtures_dir, 'hello-world.txt')
|
||||
|
||||
|
||||
download_hello_world = DownloadView.as_view(file=hello_world_file)
|
||||
|
||||
download_hello_world = DownloadView.as_view(filename=hello_world_file,
|
||||
storage=None)
|
||||
|
||||
download_document = ObjectDownloadView.as_view(model=Document)
|
||||
|
|
|
|||
|
|
@ -1,43 +1,117 @@
|
|||
import mimetypes
|
||||
import os
|
||||
import shutil
|
||||
from wsgiref.util import FileWrapper
|
||||
|
||||
from django.core.files import File
|
||||
from django.core.files.storage import DefaultStorage
|
||||
from django.http import HttpResponse
|
||||
from django.views.generic.base import View
|
||||
from django.views.generic.detail import BaseDetailView
|
||||
from django.views.static import was_modified_since
|
||||
|
||||
|
||||
class DownloadMixin(object):
|
||||
file = None
|
||||
response_class = HttpResponse
|
||||
|
||||
def get_file(self):
|
||||
"""Return a django.core.files.File object, which is to be served."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_filename(self):
|
||||
"""Return absolute filename."""
|
||||
file_obj = self.get_file()
|
||||
return file_obj.name
|
||||
|
||||
def get_file_wrapper(self):
|
||||
"""Return a wsgiref.util.FileWrapper instance for the file to serve."""
|
||||
try:
|
||||
return self.file_wrapper
|
||||
except AttributeError:
|
||||
self.file_wrapper = FileWrapper(self.get_file())
|
||||
return self.file_wrapper
|
||||
|
||||
def get_mime_type(self):
|
||||
"""Return mime-type of self.file."""
|
||||
return 'application/octet-stream'
|
||||
"""Return mime-type."""
|
||||
try:
|
||||
return self.mime_type
|
||||
except AttributeError:
|
||||
filename = self.get_filename()
|
||||
self.mime_type, self.encoding = mimetypes.guess_type(filename)
|
||||
if not self.mime_type:
|
||||
self.mime_type = 'application/octet-stream'
|
||||
return self.mime_type
|
||||
|
||||
def get_encoding(self):
|
||||
"""Return encoding of self.file."""
|
||||
try:
|
||||
return self.encoding
|
||||
except AttributeError:
|
||||
filename = self.get_filename()
|
||||
self.mime_type, self.encoding = mimetypes.guess_type(filename)
|
||||
return self.encoding
|
||||
|
||||
def get_modification_time(self):
|
||||
"""Return last modification time of self.file."""
|
||||
try:
|
||||
return self.modification_time
|
||||
except AttributeError:
|
||||
self.stat = os.stat(self.get_filename())
|
||||
self.modification_time = self.stat.st_mtime
|
||||
return self.modification_time
|
||||
|
||||
def get_size(self):
|
||||
"""Return the size (in bytes) of the file to serve."""
|
||||
try:
|
||||
return self.size
|
||||
except AttributeError:
|
||||
try:
|
||||
self.size = self.stat.st_size
|
||||
except AttributeError:
|
||||
self.size = os.path.getsize(self.get_filename())
|
||||
return self.size
|
||||
|
||||
def render_to_response(self, **response_kwargs):
|
||||
"""Returns a response with a file as attachment."""
|
||||
mime_type = self.get_mime_type()
|
||||
if isinstance(self.file, File):
|
||||
absolute_filename = self.file.path
|
||||
wrapper = FileWrapper(self.file.file)
|
||||
size = self.file.size
|
||||
else:
|
||||
absolute_filename = os.path.abspath(self.file)
|
||||
wrapper = FileWrapper(file(absolute_filename))
|
||||
size = os.path.getsize(absolute_filename)
|
||||
basename = os.path.basename(absolute_filename)
|
||||
modification_time = self.get_modification_time()
|
||||
size = self.get_size()
|
||||
# Respect the If-Modified-Since header.
|
||||
if_modified_since = self.request.META.get('HTTP_IF_MODIFIED_SINCE', None)
|
||||
if not was_modified_since(if_modified_since, modification_time, size):
|
||||
return HttpResponseNotModified(mimetype=mime_type)
|
||||
# Stream the file.
|
||||
filename = self.get_filename()
|
||||
basename = os.path.basename(filename)
|
||||
encoding = self.get_encoding()
|
||||
wrapper = self.get_file_wrapper()
|
||||
response = self.response_class(wrapper, content_type=mime_type,
|
||||
mimetype=mime_type)
|
||||
response['Content-Length'] = size
|
||||
# Do not call fsock.close() as HttpResponse needs it open
|
||||
# Garbage collector will close it
|
||||
# Do not call fsock.close() as HttpResponse needs it open.
|
||||
# Garbage collector will close it.
|
||||
response['Content-Disposition'] = 'attachment; filename=%s' % basename
|
||||
return response
|
||||
|
||||
|
||||
class DownloadView(DownloadMixin, View):
|
||||
filename = None
|
||||
storage = DefaultStorage()
|
||||
|
||||
def get_file(self):
|
||||
"""Return a file object for the file to serve."""
|
||||
try:
|
||||
return self._file
|
||||
except AttributeError:
|
||||
try:
|
||||
if self.storage:
|
||||
self._file = self.storage.open(self.filename)
|
||||
else:
|
||||
self._file = File(open(self.filename))
|
||||
return self._file
|
||||
except IOError:
|
||||
raise Http404
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
return self.render_to_response()
|
||||
|
||||
|
|
@ -45,7 +119,19 @@ class DownloadView(DownloadMixin, View):
|
|||
class ObjectDownloadView(DownloadMixin, BaseDetailView):
|
||||
file_field = 'file'
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
def get_fieldfile(self):
|
||||
self.object = self.get_object()
|
||||
self.file = getattr(self.object, self.file_field)
|
||||
try:
|
||||
return self.fieldfile
|
||||
except AttributeError:
|
||||
self.fieldfile = getattr(self.object, self.file_field)
|
||||
return self.fieldfile
|
||||
|
||||
def get_file(self):
|
||||
return self.get_fieldfile().file
|
||||
|
||||
def get_size(self):
|
||||
return self.get_fieldfile().size
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
return self.render_to_response()
|
||||
|
|
|
|||
Loading…
Reference in a new issue