diff --git a/demo/demoproject/download/tests.py b/demo/demoproject/download/tests.py index a807be7..2338599 100644 --- a/demo/demoproject/download/tests.py +++ b/demo/demoproject/download/tests.py @@ -31,20 +31,29 @@ class DownloadTestCase(TestCase): self.assertEquals(response['Content-Type'], 'text/plain; charset=utf-8') self.assertFalse('ContentEncoding' in response) - self.assertEquals(response['Content-Disposition'], - 'attachment; filename=hello-world.txt') + if is_attachment: + self.assertEquals(response['Content-Disposition'], + 'attachment; filename=hello-world.txt') + else: + self.assertFalse('Content-Disposition' in response) self.assertEqual(open(self.files['hello-world.txt']).read(), ''.join(response.streaming_content)) class PathDownloadViewTestCase(DownloadTestCase): - """Test "hello_world" view.""" + """Test "hello_world" and "hello_world_inline" views.""" def test_download_hello_world(self): """hello_world view returns hello-world.txt as attachement.""" download_url = reverse('hello_world') response = self.client.get(download_url) self.assertDownloadHelloWorld(response) + def test_download_hello_world_inline(self): + """hello_world view returns hello-world.txt as attachement.""" + download_url = reverse('hello_world_inline') + response = self.client.get(download_url) + self.assertDownloadHelloWorld(response, is_attachment=False) + class CustomPathDownloadViewTestCase(DownloadTestCase): """Test "fixture_from_path" view.""" diff --git a/demo/demoproject/download/urls.py b/demo/demoproject/download/urls.py index 24d0d12..90390ee 100644 --- a/demo/demoproject/download/urls.py +++ b/demo/demoproject/download/urls.py @@ -17,6 +17,9 @@ urlpatterns = patterns( url(r'^hello-world\.txt$', 'download_hello_world', name='hello_world'), + url(r'^hello-world-inline\.txt$', + 'download_hello_world_inline', + name='hello_world_inline'), url(r'^path/(?P[a-zA-Z0-9_-]+\.[a-zA-Z0-9]{1,4})$', 'download_fixture_from_path', name='fixture_from_path'), diff --git a/demo/demoproject/download/views.py b/demo/demoproject/download/views.py index 65075bc..cd106cd 100644 --- a/demo/demoproject/download/views.py +++ b/demo/demoproject/download/views.py @@ -31,6 +31,11 @@ fixtures_storage = FileSystemStorage(location=fixtures_dir) download_document = views.ObjectDownloadView.as_view(model=Document) +#: Same as download_document, but streamed inline, i.e. not as attachments. +download_document_inline = views.ObjectDownloadView.as_view(model=Document, + attachment=False) + + #: Pre-configured view using a storage. download_fixture_from_storage = views.StorageDownloadView.as_view( storage=fixtures_storage) @@ -42,6 +47,12 @@ download_fixture_from_storage = views.StorageDownloadView.as_view( download_hello_world = views.PathDownloadView.as_view(path=hello_world_path) +#: Direct download of one file, based on an absolute path, not as attachment. +download_hello_world_inline = views.PathDownloadView.as_view( + path=hello_world_path, + attachment=False) + + class CustomPathDownloadView(views.PathDownloadView): """Example of customized PathDownloadView.""" def get_path(self): diff --git a/demo/demoproject/nginx/tests.py b/demo/demoproject/nginx/tests.py index 9a872ef..10258da 100644 --- a/demo/demoproject/nginx/tests.py +++ b/demo/demoproject/nginx/tests.py @@ -37,3 +37,26 @@ class XAccelRedirectDecoratorTestCase(DownloadTestCase): self.assertFalse('ContentEncoding' in response) self.assertEquals(response['Content-Disposition'], 'attachment; filename=hello-world.txt') + + +class InlineXAccelRedirectTestCase(DownloadTestCase): + @temporary_media_root() + def test_response(self): + """X-Accel optimization respects ``attachment`` attribute.""" + document = Document.objects.create( + slug='hello-world', + file=File(open(self.files['hello-world.txt'])), + ) + download_url = reverse('download_document_nginx_inline', + kwargs={'slug': 'hello-world'}) + response = self.client.get(download_url) + assert_x_accel_redirect( + self, + response, + content_type="text/plain; charset=utf-8", + charset="utf-8", + attachment=False, + redirect_url="/download-optimized/document/hello-world.txt", + expires=None, + with_buffering=None, + limit_rate=None) diff --git a/demo/demoproject/nginx/urls.py b/demo/demoproject/nginx/urls.py index 547e46d..9920da8 100644 --- a/demo/demoproject/nginx/urls.py +++ b/demo/demoproject/nginx/urls.py @@ -2,7 +2,11 @@ from django.conf.urls import patterns, url -urlpatterns = patterns('demoproject.nginx.views', +urlpatterns = patterns( + 'demoproject.nginx.views', url(r'^document-nginx/(?P[a-zA-Z0-9_-]+)/$', 'download_document_nginx', name='download_document_nginx'), + url(r'^document-nginx-inline/(?P[a-zA-Z0-9_-]+)/$', + 'download_document_nginx_inline', + name='download_document_nginx_inline'), ) diff --git a/demo/demoproject/nginx/views.py b/demo/demoproject/nginx/views.py index 42263cf..34d4bbd 100644 --- a/demo/demoproject/nginx/views.py +++ b/demo/demoproject/nginx/views.py @@ -1,4 +1,6 @@ """Views.""" +from django.conf import settings + from django_downloadview.nginx import x_accel_redirect from demoproject.download import views @@ -8,3 +10,9 @@ download_document_nginx = x_accel_redirect( views.download_document, source_dir='/var/www/files', destination_url='/download-optimized') + + +download_document_nginx_inline = x_accel_redirect( + views.download_document_inline, + source_dir=settings.MEDIA_ROOT, + destination_url='/download-optimized') diff --git a/django_downloadview/nginx.py b/django_downloadview/nginx.py index bdb7131..9f5a0e8 100644 --- a/django_downloadview/nginx.py +++ b/django_downloadview/nginx.py @@ -103,11 +103,13 @@ if not hasattr(settings, 'NGINX_DOWNLOAD_MIDDLEWARE_DESTINATION_URL'): class XAccelRedirectResponse(HttpResponse): """Http response that delegates serving file to Nginx.""" def __init__(self, redirect_url, content_type, basename=None, expires=None, - with_buffering=None, limit_rate=None): + with_buffering=None, limit_rate=None, attachment=True): """Return a HttpResponse with headers for Nginx X-Accel-Redirect.""" super(XAccelRedirectResponse, self).__init__(content_type=content_type) - self.basename = basename or redirect_url.split('/')[-1] - self['Content-Disposition'] = 'attachment; filename=%s' % self.basename + if attachment: + self.basename = basename or redirect_url.split('/')[-1] + self['Content-Disposition'] = 'attachment; filename={name}'.format( + name=self.basename) self['X-Accel-Redirect'] = redirect_url self['X-Accel-Charset'] = content_type_to_charset(content_type) if with_buffering is not None: @@ -203,6 +205,13 @@ class XAccelRedirectValidator(object): else: test_case.assertEqual(header, value) + def assert_attachment(self, test_case, response, value): + header = 'Content-Disposition' + if value: + test_case.assertTrue(response[header].startswith('attachment')) + else: + test_case.assertFalse(header in response) + def assert_x_accel_redirect(test_case, response, **assertions): """Make ``test_case`` assert that ``response`` is a XAccelRedirectResponse. @@ -345,7 +354,8 @@ class BaseXAccelRedirectMiddleware(BaseDownloadMiddleware): basename=response.basename, expires=expires, with_buffering=self.with_buffering, - limit_rate=self.limit_rate) + limit_rate=self.limit_rate, + attachment=response.attachment) class XAccelRedirectMiddleware(BaseXAccelRedirectMiddleware):