Handling Multipart Responses with AIOHTTP in Python

Multipart responses enable you to send multiple data types in a single HTTP response.

They allow you to combine text, JSON, binary files, and even HTML content in one package.

In this tutorial, you’ll learn how to create, handle, and parse multipart responses using AIOHTTP.

 

 

Create Multipart Responses

To create a multipart response, you can use the aiohttp.MultipartWriter class.

This allows you to construct responses with multiple parts, each having its own headers and content.

from aiohttp import web
from aiohttp.multipart import MultipartWriter
async def handle_multipart(request):
    writer = MultipartWriter('mixed')
    response = web.StreamResponse()
    response.content_type = 'multipart/mixed'
    await response.prepare(request)

    # Add parts to the writer
    await writer.write(response)
    return response
app = web.Application()
app.router.add_get('/multipart', handle_multipart)

The MultipartWriter is initialized with the ‘mixed’ subtype.

 

Handle Different Content Types

Text Content

Add plain text content to your multipart response easily.

text_content = "Hello, this is a sample text content."
text_part = writer.append(text_content)
text_part.headers['Content-Type'] = 'text/plain'

Plain text is added as a separate part in the multipart response so the clients can extract and display text easily.

JSON Data

Include structured data in your response using JSON.

json_data = {
    "user": {
        "name": "Alice Smith",
        "email": "alice@example.com",
        "age": 28
    },
    "preferences": ["python", "asyncio", "web development"]
}
json_part = writer.append_json(json_data)

JSON data is serialized and added as a separate part, allowing for easy parsing and use by the client.

Binary Data (Files, Images)

You can include binary data such as images in your multipart response.

with open('logo.png', 'rb') as f:
    image_data = f.read()
    img_part = writer.append(image_data, {'Content-Type': 'image/png'})
    img_part.set_content_disposition('attachment', filename='company_logo.png')

Binary data is read from a file and added as a part of the multipart response.

HTML Content

You can include formatted HTML content in your multipart response.

html_content = """
<html>
<body>
    <h1>Welcome to AIOHTTP Multipart Tutorial</h1>
    <p>This is a sample HTML content in a multipart response.</p>
</body>
</html>
"""
html_part = writer.append(html_content)
html_part.headers['Content-Type'] = 'text/html'

This allows clients to render it as a formatted web page if needed.

 

Read Multipart Responses

You can use aiohttp.MultipartReader to parse multipart responses on the client side.

import aiohttp
async def fetch_multipart():
    async with aiohttp.ClientSession() as session:
        async with session.get('http://example.com/multipart') as resp:
            reader = aiohttp.MultipartReader.from_response(resp)
            # Process parts here

This code prepares a client to receive and parse a multipart response using MultipartReader to handle the incoming data.

Extract Different Parts from the Response

You can process each part of the multipart response based on its content type.

async def process_parts(reader):
    async for part in reader:
        if part.headers['Content-Type'] == 'text/plain':
            text = await part.text()
            print(f"Text content: {text}")
        elif part.headers['Content-Type'] == 'application/json':
            json_data = await part.json()
            print(f"JSON data: {json_data}")
        elif part.headers['Content-Type'] == 'image/png':
            filename = part.filename or 'image.png'
            with open(filename, 'wb') as f:
                f.write(await part.read())
            print(f"Saved image: {filename}")
        elif part.headers['Content-Type'] == 'text/html':
            html = await part.text()
            print(f"HTML content: {html[:100]}...")  # Print first 100 chars

Output:

Text content: Hello, this is a sample text content.
JSON data: {'user': {'name': 'Alice Smith', 'email': 'alice@example.com', 'age': 28}, 'preferences': ['python', 'asyncio', 'web development']}
Saved image: company_logo.png
HTML content: <html><body><h1>Welcome to AIOHTTP Multipart Tutorial</h1><p>This is a sample HTML content in a...

This code processes each part of the multipart response according to its content type.

 

Nested Multipart Responses

You can handle nested multipart responses by recursively processing parts.

async def process_nested_multipart(part):
    if part.headers['Content-Type'].startswith('multipart/'):
        nested_reader = await part.multipart()
        async for nested_part in nested_reader:
            await process_nested_multipart(nested_part)
    else:
        # Process the part as before
        pass
async def handle_response(resp):
    reader = aiohttp.MultipartReader.from_response(resp)
    async for part in reader:
        await process_nested_multipart(part)

This recursive method allows you to handle multipart responses of any depth.

 

Compress Individual Parts

You can compress large parts to reduce response size by comprising the data and setting the suitable encoding:

import zlib
def compress_data(data):
    return zlib.compress(data.encode() if isinstance(data, str) else data)
async def compressed_multipart(request):
    writer = MultipartWriter()
    large_text = "A" * 10000  # 10KB of data
    compressed = compress_data(large_text)
    part = writer.append(compressed)
    part.headers['Content-Type'] = 'text/plain'
    part.headers['Content-Encoding'] = 'deflate'
    response = web.StreamResponse()
    response.content_type = 'multipart/mixed'
    await response.prepare(request)
    await writer.write(response)
    return response

Compressing large parts of a multipart response can significantly reduce the amount of data transferred.

Leave a Reply

Your email address will not be published. Required fields are marked *