Commit 3d2cae08 authored by Collin Anderson's avatar Collin Anderson Committed by Tim Graham
Browse files

Fixed #24072 -- Added FileResponse for streaming binary files.

parent 05f702b9
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -197,6 +197,8 @@ class WSGIHandler(base.BaseHandler):
        for c in response.cookies.values():
            response_headers.append((str('Set-Cookie'), str(c.output(header=''))))
        start_response(force_str(status), response_headers)
        if getattr(response, 'file_to_stream', None) is not None and environ.get('wsgi.file_wrapper'):
            response = environ['wsgi.file_wrapper'](response.file_to_stream)
        return response


+5 −3
Original line number Diff line number Diff line
from django.http.cookie import SimpleCookie, parse_cookie
from django.http.request import (HttpRequest, QueryDict,
    RawPostDataException, UnreadablePostError, build_request_repr)
from django.http.response import (HttpResponse, StreamingHttpResponse,
from django.http.response import (
    HttpResponse, StreamingHttpResponse, FileResponse,
    HttpResponseRedirect, HttpResponsePermanentRedirect,
    HttpResponseNotModified, HttpResponseBadRequest, HttpResponseForbidden,
    HttpResponseNotFound, HttpResponseNotAllowed, HttpResponseGone,
    HttpResponseServerError, Http404, BadHeaderError, JsonResponse)
    HttpResponseServerError, Http404, BadHeaderError, JsonResponse,
)
from django.http.utils import fix_location_header, conditional_content_removal

__all__ = [
@@ -16,5 +18,5 @@ __all__ = [
    'HttpResponseBadRequest', 'HttpResponseForbidden', 'HttpResponseNotFound',
    'HttpResponseNotAllowed', 'HttpResponseGone', 'HttpResponseServerError',
    'Http404', 'BadHeaderError', 'fix_location_header', 'JsonResponse',
    'conditional_content_removal',
    'FileResponse', 'conditional_content_removal',
]
+19 −0
Original line number Diff line number Diff line
@@ -417,6 +417,9 @@ class StreamingHttpResponse(HttpResponseBase):

    @streaming_content.setter
    def streaming_content(self, value):
        self._set_streaming_content(value)

    def _set_streaming_content(self, value):
        # Ensure we can never iterate on "value" more than once.
        self._iterator = iter(value)
        if hasattr(value, 'close'):
@@ -429,6 +432,22 @@ class StreamingHttpResponse(HttpResponseBase):
        return b''.join(self.streaming_content)


class FileResponse(StreamingHttpResponse):
    """
    A streaming HTTP response class optimized for files.
    """
    block_size = 4096

    def _set_streaming_content(self, value):
        if hasattr(value, 'read'):
            self.file_to_stream = value
            filelike = value
            value = iter(lambda: filelike.read(self.block_size), b'')
        else:
            self.file_to_stream = None
        super(FileResponse, self)._set_streaming_content(value)


class HttpResponseRedirectBase(HttpResponse):
    allowed_schemes = ['http', 'https', 'ftp']

+2 −3
Original line number Diff line number Diff line
@@ -11,7 +11,7 @@ import posixpath
import re

from django.http import (Http404, HttpResponse, HttpResponseRedirect,
    HttpResponseNotModified, StreamingHttpResponse)
    HttpResponseNotModified, FileResponse)
from django.template import loader, Template, Context, TemplateDoesNotExist
from django.utils.http import http_date, parse_http_date
from django.utils.six.moves.urllib.parse import unquote
@@ -63,8 +63,7 @@ def serve(request, path, document_root=None, show_indexes=False):
        return HttpResponseNotModified()
    content_type, encoding = mimetypes.guess_type(fullpath)
    content_type = content_type or 'application/octet-stream'
    response = StreamingHttpResponse(open(fullpath, 'rb'),
                                     content_type=content_type)
    response = FileResponse(open(fullpath, 'rb'), content_type=content_type)
    response["Last-Modified"] = http_date(statobj.st_mtime)
    if stat.S_ISREG(statobj.st_mode):
        response["Content-Length"] = statobj.st_size
+18 −0
Original line number Diff line number Diff line
@@ -998,3 +998,21 @@ Attributes
.. attribute:: StreamingHttpResponse.streaming

    This is always ``True``.

FileResponse objects
====================

.. versionadded:: 1.8

.. class:: FileResponse

:class:`FileResponse` is a subclass of :class:`StreamingHttpResponse` optimized
for binary files. It uses `wsgi.file_wrapper`_ if provided by the wsgi server,
otherwise it streams the file out in small chunks.

.. _wsgi.file_wrapper: https://www.python.org/dev/peps/pep-3333/#optional-platform-specific-file-handling

``FileResponse`` expects a file open in binary mode like so::

    >>> from django.http import FileResponse
    >>> response = FileResponse(open('myfile.png', 'rb'))
Loading