You should never call res.end(huge buffer). res.end guarantees sending so backpressure will probably spike. Instead you should use res.try_end to stream huge data part by part. Use in combination with res.on_writable and res.on_aborted callbacks.
For simplicity, you can use res.send_chunk
, this will return an Future and use res.on_writable
and res.on_aborted
for you.
Using send_chunk:
async def home(res, req):
res.write_header("Content-Type", "audio/mpeg")
filename = "./file_example_MP3_5MG.mp3"
total = os.stat(filename).st_size
async with aiofiles.open(filename, "rb") as fd:
while not res.aborted:
buffer = await fd.read(16384) #16kb buffer
(ok, done) = await res.send_chunk(buffer, total)
if not ok or done: #if cannot send probably aborted
break
If you want to understand res.send_chunk
, check out the implementation:
def send_chunk(self, buffer, total_size):
self._chunkFuture = self.loop.create_future()
self._lastChunkOffset = 0
def is_aborted(self):
self.aborted = True
try:
if not self._chunkFuture.done():
self._chunkFuture.set_result(
(False, True)
) # if aborted set to done True and ok False
except:
pass
def on_writeble(self, offset):
# Here the timeout is off, we can spend as much time before calling try_end we want to
(ok, done) = self.try_end(
buffer[offset - self._lastChunkOffset : :], total_size
)
if ok:
self._chunkFuture.set_result((ok, done))
return ok
self.on_writable(on_writeble)
self.on_aborted(is_aborted)
if self.aborted:
self._chunkFuture.set_result(
(False, True)
) # if aborted set to done True and ok False
return self._chunkFuture
(ok, done) = self.try_end(buffer, total_size)
if ok:
self._chunkFuture.set_result((ok, done))
return self._chunkFuture
# failed to send chunk
self._lastChunkOffset = self.get_write_offset()
return self._chunkFuture