Add file_stream response handler

For streaming large static files
Like `file()` but breaks the file into chunks and sends it with a `StreamingHTTPResponse`
Chunk size is configurable, but defaults to 4k, this seemed to be the sweet spot in my testing.
Also supports ContentRange same as `file()` does.
This commit is contained in:
ashleysommer 2017-05-18 18:04:28 +10:00
parent 0e4aaf8856
commit e155fe403d

View File

@ -310,6 +310,55 @@ async def file(location, mime_type=None, headers=None, _range=None):
body_bytes=out_stream) body_bytes=out_stream)
async def file_stream(location, chunk_size=4096, mime_type=None, headers=None,
_range=None):
"""Return a streaming response object with file data.
:param location: Location of file on system.
:param chunk_size: The size of each chunk in the stream (in bytes)
:param mime_type: Specific mime_type.
:param headers: Custom Headers.
:param _range:
"""
filename = path.split(location)[-1]
_file = await open_async(location, mode='rb')
async def _streaming_fn(response):
nonlocal _file, chunk_size
try:
if _range:
chunk_size = min((_range.size, chunk_size))
await _file.seek(_range.start)
to_send = _range.size
while to_send > 0:
content = await _file.read(chunk_size)
if len(content) < 1:
break
to_send -= len(content)
response.write(content)
else:
while True:
content = await _file.read(chunk_size)
if len(content) < 1:
break
response.write(content)
except Exception as e:
print(e)
finally:
await _file.close()
return # Returning from this fn closes the stream
mime_type = mime_type or guess_type(filename)[0] or 'text/plain'
if _range:
headers['Content-Range'] = 'bytes %s-%s/%s' % (
_range.start, _range.end, _range.total)
return StreamingHTTPResponse(streaming_fn=_streaming_fn,
status=200,
headers=headers,
content_type=mime_type)
def stream( def stream(
streaming_fn, status=200, headers=None, streaming_fn, status=200, headers=None,
content_type="text/plain; charset=utf-8"): content_type="text/plain; charset=utf-8"):