diff --git a/opentech/apply/funds/urls.py b/opentech/apply/funds/urls.py index dd75bcd4a84bc64ab50dff8c52f85ed9ae0a0155..386993422d70d095bd1c10dc336fd9c0616fdc74 100644 --- a/opentech/apply/funds/urls.py +++ b/opentech/apply/funds/urls.py @@ -12,7 +12,7 @@ from .views import ( SubmissionOverviewView, SubmissionSealedView, SubmissionDeleteView, - SubmissionPrivateMediaRedirectView, + SubmissionPrivateMediaView, ) from .api_views import ( CommentEdit, @@ -39,7 +39,7 @@ submission_urls = ([ path('all/', SubmissionListView.as_view(), name="list"), path( 'documents/submission/<int:submission_id>/<uuid:field_id>/<str:file_name>/', - SubmissionPrivateMediaRedirectView.as_view(), name='private_media_redirect' + SubmissionPrivateMediaView.as_view(), name='serve_private_media' ), path('<int:pk>/', include([ path('', SubmissionDetailView.as_view(), name="detail"), diff --git a/opentech/apply/funds/views.py b/opentech/apply/funds/views.py index 135988c9cc3ec3bb77221f58b9d21b016a276c01..a5de558a1c6bf183312f2fc79f8bbc1b6b91bd84 100644 --- a/opentech/apply/funds/views.py +++ b/opentech/apply/funds/views.py @@ -1,4 +1,6 @@ +import mimetypes from copy import copy +from wsgiref.util import FileWrapper from django.conf import settings from django.contrib.auth.decorators import login_required, permission_required @@ -8,13 +10,13 @@ from django.contrib import messages from django.core.exceptions import PermissionDenied from django.core.files.storage import get_storage_class from django.db.models import Count, F, Q -from django.http import HttpResponseRedirect, Http404 +from django.http import HttpResponseRedirect, Http404, StreamingHttpResponse from django.shortcuts import get_object_or_404 from django.urls import reverse_lazy from django.utils.decorators import method_decorator from django.utils.text import mark_safe from django.utils.translation import ugettext_lazy as _ -from django.views.generic import DetailView, FormView, ListView, UpdateView, DeleteView, RedirectView +from django.views.generic import DetailView, FormView, ListView, UpdateView, DeleteView, View from django_filters.views import FilterView from django_tables2.views import SingleTableMixin @@ -831,15 +833,32 @@ class SubmissionDeleteView(DeleteView): return response -class SubmissionPrivateMediaRedirectView(UserPassesTestMixin, RedirectView): +class SubmissionPrivateMediaView(UserPassesTestMixin, View): - def get_redirect_url(self, *args, **kwargs): + def get(self, *args, **kwargs): submission_id = kwargs['submission_id'] field_id = kwargs['field_id'] file_name = kwargs['file_name'] file_name_with_path = f'submission/{submission_id}/{field_id}/{file_name}' - return submission_storage.url(file_name_with_path) + submission_file = submission_storage.open(file_name_with_path) + wrapper = FileWrapper(submission_file) + encoding_map = { + 'bzip2': 'application/x-bzip', + 'gzip': 'application/gzip', + 'xz': 'application/x-xz', + } + content_type, encoding = mimetypes.guess_type(file_name) + # Encoding isn't set to prevent browsers from automatically uncompressing files. + content_type = encoding_map.get(encoding, content_type) + content_type = content_type or 'application/octet-stream' + # From Django 2.1, we can use FileResponse instead of StreamingHttpResponse + response = StreamingHttpResponse(wrapper, content_type=content_type) + + response['Content-Disposition'] = f'filename={file_name}' + response['Content-Length'] = submission_file.size + + return response def test_func(self): submission_id = self.kwargs['submission_id']