Skip to content

Commit

Permalink
FileResponse works with files asynchronously (#3476)
Browse files Browse the repository at this point in the history
* FileResponse works with files asynchronously

* FileResponse: the 'prepare' method works with files asynchronously
  • Loading branch information
Tolmachofof authored and asvetlov committed Jan 2, 2019
1 parent 9b0fd99 commit c77c058
Show file tree
Hide file tree
Showing 3 changed files with 13 additions and 8 deletions.
1 change: 1 addition & 0 deletions CHANGES/3313.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
FileResponse from web_fileresponse.py uses a ThreadPoolExecutor to work with files asynchronously.
1 change: 1 addition & 0 deletions CONTRIBUTORS.txt
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ Eric Sheng
Erich Healy
Eugene Chernyshov
Eugene Naydenov
Eugene Tolmachev
Evert Lammerts
FichteFoll
Frederik Gladhorn
Expand Down
19 changes: 11 additions & 8 deletions aiohttp/web_fileresponse.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,22 +179,24 @@ async def _sendfile_fallback(self, request: 'BaseRequest',
# os.sendfile() system call. This should be used on systems
# that don't support the os.sendfile().

# To avoid blocking the event loop & to keep memory usage low,
# fobj is transferred in chunks controlled by the
# constructor's chunk_size argument.
# To keep memory usage low,fobj is transferred in chunks
# controlled by the constructor's chunk_size argument.

writer = await super().prepare(request)
assert writer is not None

chunk_size = self._chunk_size
loop = asyncio.get_event_loop()

chunk = fobj.read(chunk_size)
chunk = await loop.run_in_executor(None, fobj.read, chunk_size)
while chunk:
await writer.write(chunk)
count = count - chunk_size
if count <= 0:
break
chunk = fobj.read(min(chunk_size, count))
chunk = await loop.run_in_executor(
None, fobj.read, min(chunk_size, count)
)

await writer.drain()
return writer
Expand All @@ -218,7 +220,8 @@ async def prepare(
filepath = gzip_path
gzip = True

st = filepath.stat()
loop = asyncio.get_event_loop()
st = await loop.run_in_executor(None, filepath.stat)

modsince = request.if_modified_since
if modsince is not None and st.st_mtime <= modsince.timestamp():
Expand Down Expand Up @@ -334,8 +337,8 @@ async def prepare(
self.headers[hdrs.CONTENT_RANGE] = 'bytes {0}-{1}/{2}'.format(
real_start, real_start + count - 1, file_size)

with filepath.open('rb') as fobj:
with (await loop.run_in_executor(None, filepath.open, 'rb')) as fobj:
if start: # be aware that start could be None or int=0 here.
fobj.seek(start)
await loop.run_in_executor(None, fobj.seek, start)

return await self._sendfile(request, fobj, count)

0 comments on commit c77c058

Please sign in to comment.