How to Mock Content-Disposition and Extract Filename in Python

Parse and mock Content-Disposition headers in Python to extract filenames, handling both plain and RFC 5987 encoded values.

Easy Python 3.9+ Aug 9, 2026 API design & gRPC 15 views 0 copies

Python code

50 lines
Python 3.9+
import os
from pathlib import Path
import re
from unittest.mock import patch

def get_filename_from_content_disposition(header_value):
    """
    Extract filename from a Content-Disposition header value.
    Supports both filename and filename* parameters (RFC 5987).
    """
    if not header_value:
        return None
    
    # Match traditional filename="..."
    simple_match = re.search(r'filename="?([^";]+)"?', header_value, re.IGNORECASE)
    
    # Match encoded filename*="UTF-8''..."
    encoded_match = re.search(r"filename\*\s*=\s*UTF-8''([^;]+)", header_value, re.IGNORECASE)
    
    if encoded_match:
        from urllib.parse import unquote
        return unquote(encoded_match.group(1))
    elif simple_match:
        return simple_match.group(1)
    return None


def mock_download_response(headers_dict):
    """
    Simulate reading Content-Disposition from a mock HTTP response.
    Returns the filename if present, else 'downloaded_file'.
    """
    content_disposition = headers_dict.get('Content-Disposition', '')
    filename = get_filename_from_content_disposition(content_disposition)
    return filename or 'downloaded_file'


if __name__ == "__main__":
    # Test cases with mock headers
    test_cases = [
        {'Content-Disposition': 'attachment; filename="report.pdf"'},
        {'Content-Disposition': "attachment; filename*=UTF-8''%E2%82%AC%20rates.txt"},
        {'Content-Disposition': ''},
        {}
    ]
    
    for headers in test_cases:
        result = mock_download_response(headers)
        print(f"Headers: {headers}")
        print(f"Extracted filename: {result}\n")

Output

stdout
Headers: {'Content-Disposition': 'attachment; filename="report.pdf"'}
Extracted filename: report.pdf

Headers: {'Content-Disposition': "attachment; filename*=UTF-8''%E2%82%AC%20rates.txt"}
Extracted filename: € rates.txt

Headers: {'Content-Disposition': ''}
Extracted filename: downloaded_file

Headers: {}
Extracted filename: downloaded_file

How it works

This function uses two regex patterns to match both the simple filename parameter and the RFC 5987 filename* form. The encoded variant is URL-decoded with urllib.parse.unquote to get the original Unicode characters. The mock response simulates HTTP header dicts, and falls back to a default filename when no match is found. This pattern mirrors how real clients (like requests or urllib) parse download headers.

Common mistakes

  • Using a single regex that misses the `filename*` variant when both are present
  • Forgetting to URL-decode the `filename*` value before returning it
  • Assuming every response has a Content-Disposition header without a fallback
  • Case-matching headers without using `str.lower()` on keys or values

Variations

  1. Return a tuple like `(filename, disposition_type)` to capture attachment vs inline
  2. Use `email.message.Message` to parse the header instead of regex for full RFC compliance

Real-world use cases

  • Automating file download tests in a CI pipeline by mocking response headers
  • Sanitizing filenames from a third-party API before saving to disk to avoid path traversal
  • Building a scraper that downloads files and names them from Content-Disposition

Sponsored

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from API design & gRPC

Related tutorials and quizzes for this topic.