Using aiohttp's MultipartWriter with StreamResponse

1.7k views Asked by At

I am trying to create a data endpoint that streams either the entirety of a file or responds appropriately to range requests. Streaming the whole file seems understandable, but it's not clear to me how to deal with range requests. Particularly, I can't see how aiohttp.MultipartWriter can write to a StreamResponse.

Here's an abstracted form of my code, so far:

from aiohttp.web import Request, StreamResponse
from aiohttp.multipart import MultipartWriter

async def data_handler(req:Request) -> StreamResponse:
    is_range_request = "Range" in req.headers

    with open("my_big_file", "rb") as f:
        if is_range_request:
            status_code = 202
            content_type = "multipart/bytes"
        else:
            status_code = 200
            content_type = "application/octet-stream"

        resp = SteamResponse(status=status_code, headers={"Content-Type": content_type})
        resp.enable_chunked_encoding()
        resp.enable_compression()

        await resp.prepare(req)

        if is_range_request:
            # _parse_range_header :: str -> List[ByteRange]
            # ByteRange = Tuple[int, int]  i.e., "from" and "to", inclusive
            ranges = _parse_range_header(req.headers["Range"])

            mpwriter = MultipartWriter("bytes")

            for r in ranges:
                range_from, range_to = r
                range_size = (range_to - range_from) + 1
                range_header = {"Content-Type": "application/octet-stream"}

                # FIXME Won't this block?
                f.seek(range_from)
                mpwriter.append(f.read(range_size), range_header)

            # TODO Write to response. How?...

        else:
            while True:
                data = f.read(8192)
                if not data:
                    await resp.drain()
                    break

                 resp.write(data)

    return resp

This also doesn't return the response until it gets to the end. This doesn't seem correct to me: How does an upstream call know what's going on until the response is returned; or is the asyncio stuff doing this for me automagically?

0

There are 0 answers