How to Use aiohttp FileResponse to Serve Files in Python

In this tutorial, you’ll learn how to use aiohttp FileResponse to serve files in your Python web applications.

FileResponse allows you to stream files asynchronously so you can handle large files or serve many concurrent requests.

 

 

Basic Usage of FileResponse

Syntax and parameters

To use FileResponse, you need to import it from aiohttp. Here’s a basic example of its syntax:

from aiohttp import web
async def handle_file(request):
    return web.FileResponse('file.txt')
app = web.Application()
app.router.add_get('/file', handle_file)
web.run_app(app)

This code sets up a simple aiohttp server that serves a file when you access the ‘/file’ route.

Create a simple file server

Let’s create a more complete example of a file server:

from aiohttp import web
import os
async def handle_file(request):
    file_path = request.match_info.get('filepath', 'index.html')
    full_path = os.path.join('files', file_path)
    if os.path.exists(full_path):
        return web.FileResponse(full_path)
    return web.Response(text="File not found", status=404)
app = web.Application()
app.router.add_get('/{filepath:.*}', handle_file)
if __name__ == '__main__':
    web.run_app(app)

This code creates a simple file server that serves files from a ‘files’ directory.

When you run this script and access ‘http://localhost:8080/file.txt’, it will serve the ‘file.txt’ file from the ‘files’ directory. If the file doesn’t exist, it returns a 404 error.

 

Customize FileResponse Behavior

Set Custom Headers

You can set custom headers when using FileResponse:

async def serve_with_custom_header(request):
    return web.FileResponse(
        'files/data.csv',
        headers={'X-Custom-Header': 'Some Value'}
    )
app = web.Application()
app.router.add_get('/{filepath:.*}', serve_with_custom_header)
if __name__ == '__main__':
    web.run_app(app)

This will serve the ‘data.csv’ file with an additional custom header.

Control Chunk Size (for large files)

You can control the chunk size used when streaming the file:

async def serve_large_file(request):
    return web.FileResponse(
        'files/large_file.zip',
        chunk_size=8192  # 8KB chunks
    )
app = web.Application()
app.router.add_get('/{filepath:.*}', serve_large_file)
if __name__ == '__main__':
    web.run_app(app)

This serves a large ZIP file in 8KB chunks, which is useful for large files.

 

Prevent Directory Traversal Attacks

To prevent directory traversal attacks, always sanitize and validate file paths:

def sanitize_filename(filename):
    return ''.join(c for c in filename if c.isalnum() or c in '._-')
async def serve_sanitized_file(request):
    filename = sanitize_filename(request.match_info['filename'])
    file_path = os.path.join('files', filename)
    if os.path.isfile(file_path):
        return web.FileResponse(file_path)
    return web.Response(text="File not found", status=404)
app = web.Application()
app.router.add_get('/{filename:.*}', serve_sanitized_file)  # Corrected route
if __name__ == '__main__':
    web.run_app(app)

This function sanitizes the filename before serving the file.

 

Serve Dynamically Generated Files

You can use FileResponse to serve dynamically generated files:

import tempfile
async def serve_dynamic_file(request):
    with tempfile.NamedTemporaryFile(mode='w', delete=False) as temp_file:
        temp_file.write("This is dynamically generated content.")
    return web.FileResponse(temp_file.name)
app = web.Application()
app.router.add_get('/', serve_dynamic_file)
if __name__ == '__main__':
    web.run_app(app)

This creates a temporary file with dynamic content and serves it.

 

When to use FileResponse Over Alternatives

Use FileResponse when you need:
– Dynamic file serving
– Custom headers or behavior
– To serve files from outside your static directory
– To implement custom access control

Leave a Reply

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