Ticket #7894: file_wrapper_response2.diff

File file_wrapper_response2.diff, 6.0 KB (added by graham.carlyle@…, 7 years ago)

Updated patch with API updated to allows mod_python support and optional length & offset

  • django/http/__init__.py

     
    424424    def __init__(self, *args, **kwargs):
    425425        HttpResponse.__init__(self, *args, **kwargs)
    426426
     427
     428class FileWrapper(object):
     429    def __init__(self, file_path, block_size, byte_count, offset):
     430        self.__dict__.update(locals())
     431
     432    def __iter__(self):
     433        self.file = open(self.file_path, 'rb')
     434        self.close = self.file.close
     435        if self.offset:
     436            self.file.seek(self.offset)
     437        self.bytes_left = self.byte_count
     438        return self
     439       
     440    def next(self):
     441        if self.bytes_left > 0:
     442            data_size = min(self.block_size, self.bytes_left)
     443            data = self.file.read(data_size)
     444            if data:
     445                self.bytes_left -= data_size
     446                return data
     447        raise StopIteration
     448
     449
     450class HttpResponseFileWrapper(HttpResponse):
     451    def __init__(self, file_path, block_size=8192, length=None, offset=0, **kwargs):
     452        content_length = length or os.path.getsize(file_path)
     453        HttpResponse.__init__(self, content=FileWrapper(file_path, block_size, content_length, offset), **kwargs)
     454        self.file_path = file_path
     455        self.block_size = block_size
     456        self.length = length
     457        self.offset = offset
     458        self['Content-Length'] = content_length
     459
     460
    427461# A backwards compatible alias for HttpRequest.get_host.
    428462def get_host(request):
    429463    return request.get_host()
  • django/core/servers/basehttp.py

     
    312312        in the event loop to iterate over the data, and to call
    313313        'self.close()' once the response is finished.
    314314        """
    315         if not self.result_is_file() and not self.sendfile():
     315        if not self.result_is_file() or not self.sendfile():
    316316            for data in self.result:
    317317                self.write(data)
    318318            self.finish_content()
  • django/core/handlers/wsgi.py

     
    11from threading import Lock
     2import os
    23from pprint import pformat
    34try:
    45    from cStringIO import StringIO
     
    194195    initLock = Lock()
    195196    request_class = WSGIRequest
    196197
     198    def wsgi_adaptor_honours_content_length(self, environ):
     199        return 'mod_wsgi.callable_object' in environ
     200   
    197201    def __call__(self, environ, start_response):
    198202        from django.conf import settings
    199203
     
    232236        for c in response.cookies.values():
    233237            response_headers.append(('Set-Cookie', str(c.output(header=''))))
    234238        start_response(status, response_headers)
    235         return response
     239        if (isinstance(response, http.HttpResponseFileWrapper) and
     240            'wsgi.file_wrapper'in environ and
     241            (response.length is None or
     242             self.wsgi_adaptor_honours_content_length(environ) or
     243             response.length == os.path.getsize(response.file_path))):
     244            f = open(response.file_path, 'rb')
     245            if response.offset:
     246                f.seek(response.offset)
     247            return environ['wsgi.file_wrapper'](f, response.block_size)
     248        else:
     249            return response
    236250
  • django/core/handlers/modpython.py

     
    199199            req.headers_out.add('Set-Cookie', c.output(header=''))
    200200        req.status = response.status_code
    201201        try:
    202             for chunk in response:
    203                 req.write(chunk)
     202            if isinstance(response, http.HttpResponseFileWrapper):
     203                if response.length is None:
     204                    length = -1
     205                else:
     206                    length = response.length
     207                req.sendfile(response.file_path, response.offset, length)
     208            else:
     209                for chunk in response:
     210                    req.write(chunk)
    204211        finally:
    205212            response.close()
    206 
    207213        return 0 # mod_python.apache.OK
    208214
    209215def handler(req):
  • tests/regressiontests/httpwrappers/tests.py

     
    426426Traceback (most recent call last):
    427427...
    428428UnicodeEncodeError: ..., HTTP response headers must be in US-ASCII format
    429  
     429
     430
     431###########################
     432# HttpResponseFileWrapper #
     433###########################
     434
     435Get the path of the test file
     436
     437>>> import os
     438>>> test_file_path = os.path.join(os.path.dirname(__file__), 'test_file.txt')
     439
     440Create a response with the file path
     441
     442>>> r = HttpResponseFileWrapper(test_file_path, mimetype='text/plain')
     443
     444The response's content defaults to an iterator over the file data
     445
     446>>> ''.join(list(r))
     447'123456789 Heres some data!'
     448
     449An offset and length can be provided limit the data read from the file
     450
     451>>> r = HttpResponseFileWrapper(test_file_path, offset=2, length=5)
     452>>> ''.join(list(r))
     453'34567'
     454
     455A block_size can also be passed in as a suggestion for the block size to read
     456
     457>>> r = HttpResponseFileWrapper(test_file_path, offset=2, length=5, block_size=4024)
     458
     459Handler can choose to deliver the file's data using alternative means by using the response's properties rather than treating it as an iterator
     460
     461>>> r.file_path == test_file_path
     462True
     463
     464>>> r.offset
     4652
     466
     467>>> r.length
     4685
     469
     470>>> r.block_size
     4714024
     472
    430473"""
    431474
    432 from django.http import QueryDict, HttpResponse
     475from django.http import QueryDict, HttpResponse, HttpResponseFileWrapper
    433476
    434477if __name__ == "__main__":
    435478    import doctest
Back to Top