Source code for lback.middlewares.media_files_middleware
import os
import mimetypes
import logging
from lback.core.response import Response
from lback.core.base_middleware import BaseMiddleware
logger = logging.getLogger(__name__)
[docs]
class MediaFilesMiddleware(BaseMiddleware):
"""
Middleware to serve user-uploaded media files during development.
Only active when config.DEBUG is True.
It uses UPLOAD_URL and UPLOAD_FOLDER from the application's configuration.
"""
[docs]
def process_request(self, request):
"""
Processes the incoming request to check if it's for a media file.
If it is and the file exists, serves the file directly.
Args:
request: The incoming request object. Expected to have 'path' and 'config' attributes.
Returns:
A Response object if a media file is served, otherwise None
to pass the request to the next middleware.
"""
config = getattr(request, 'config', None)
if config is None or not getattr(config, 'DEBUG', False):
logger.debug("MediaFilesMiddleware skipped: DEBUG is not True or config is missing.")
return None
upload_url = getattr(config, 'UPLOAD_URL', '/media/uploads/')
upload_folder_relative = getattr(config, 'UPLOAD_FOLDER', 'media/uploads')
base_dir = getattr(config, 'BASE_DIR', os.getcwd())
upload_root = os.path.join(base_dir, upload_folder_relative)
if not upload_url.endswith('/'):
upload_url += '/'
if request.path.startswith(upload_url):
relative_media_path = request.path[len(upload_url):]
logger.debug(f"Attempting to serve media file: {relative_media_path}")
media_file_path = os.path.join(upload_root, relative_media_path)
abs_upload_root = os.path.abspath(upload_root)
abs_media_file_path = os.path.abspath(media_file_path)
if not abs_media_file_path.startswith(abs_upload_root):
logger.warning(f"Attempted directory traversal detected for media file: {request.path}")
return None
if os.path.exists(abs_media_file_path) and os.path.isfile(abs_media_file_path):
try:
mime_type, _ = mimetypes.guess_type(abs_media_file_path)
if mime_type is None:
mime_type = 'application/octet-stream'
with open(abs_media_file_path, 'rb') as f:
file_content = f.read()
logger.info(f"Served media file: {abs_media_file_path}")
return Response(file_content, status_code=200, headers={'Content-Type': mime_type})
except IOError as e:
logger.error(f"Error reading media file {abs_media_file_path}: {e}", exc_info=True)
return None
else:
logger.debug(f"Media file not found or is not a file: {abs_media_file_path}")
return None
logger.debug(f"Request path '{request.path}' does not match UPLOAD_URL '{upload_url}'.")
return None
[docs]
def process_response(self, request, response):
"""
Processes the outgoing response. For media files, this middleware usually
returns a response directly, so this method might not be called for served files.
It's included for consistency with BaseMiddleware.
Args:
request: The incoming request object.
response: The outgoing response object.
Returns:
The response object.
"""
logger.debug("MediaFilesMiddleware process_response called.")
return response