[docs]
def guess_file_mimetype(filename: str) -> str:
"""
Determine the MIME type of a file based on its filename or content.
This function first attempts to guess the MIME type using the file's
extension by leveraging the `mimetypes` module. If the MIME type could not
be determined from the filename, it reads the file content and attempts to
infer the MIME type from the data.
Args:
filename (str): The path to the file for which the MIME type needs to be determined.
Returns:
str: The determined MIME type of the file. Defaults to 'application/octet-stream' if the type cannot be determined.
"""
import mimetypes
mimetype = None
if filename:
# Attempt to guess the MIME type based on the file extension
mimetype, _ = mimetypes.guess_type(filename)
return mimetype
[docs]
def guess_data_mimetype(data: bytes) -> str:
"""
Determine the MIME type of the provided content or file based on its
initial bytes (magic numbers).
Args:
data (bytes): The input data for which the MIME type needs to be determined.
Returns:
str: The determined MIME type of the input data.
"""
if not data:
return "application/octet-stream"
html_tags = (
b"<html",
b"<!DOCTYPE html",
b"<head",
b"<body",
b"<title",
b"<h1",
b"<div",
b"<span",
b"<p",
b"<a ",
b"<img ",
b"<script",
b"<style",
b"<meta",
b"<link",
b"<form",
b"<table",
b"<tr",
b"<td",
b"<th",
b"<ul",
b"<ol",
b"<li",
b"<header",
b"<footer",
b"<nav",
b"<section",
b"<article",
b"<aside",
b"<main",
b"<figure",
b"<figcaption",
b"<blockquote",
b"<pre",
b"<code",
b"<canvas",
b"<svg",
b"<br",
b"<b",
)
header = data[:512]
# Images
if data.startswith(b"\xFF\xD8\xFF"):
return "image/jpeg"
elif data.startswith(b"\x89PNG\r\n\x1A\n"):
return "image/png"
elif data.startswith((b"GIF87a", b"GIF89a")):
return "image/gif"
elif data.startswith(b"BM"):
return "image/bmp"
elif len(data) >= 12 and data.startswith(b"RIFF") and data[8:12] == b"WEBP":
return "image/webp"
elif data.startswith(b"\x00\x00\x01\x00"):
return "image/vnd.microsoft.icon"
elif (
len(data) >= 12
and data[4:8] == b"ftyp"
and data[8:12] in (
b"heic",
b"heix",
b"hevc",
b"hevx",
b"mif1",
b"msf1",
b"avif",
)
):
return "image/avif" if data[8:12] == b"avif" else "image/heif"
elif data.startswith((b"II*\x00", b"MM\x00*")):
return "image/tiff"
elif b"<svg" in header:
return "image/svg+xml"
# Documents
elif data.startswith(b"%PDF-"):
return "application/pdf"
elif data.startswith(b"\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1"):
return "application/msword"
# Archives / Compression
elif data.startswith((b"PK\x03\x04", b"PK\x05\x06", b"PK\x07\x08")):
return "application/zip"
elif data.startswith(b"\x1F\x8B"):
return "application/gzip"
elif data.startswith(b"BZh"):
return "application/x-bzip2"
elif data.startswith(b"\xFD7zXZ\x00"):
return "application/x-xz"
elif data.startswith(b"7z\xBC\xAF\x27\x1C"):
return "application/x-7z-compressed"
elif data.startswith(b"Rar!\x1A\x07"):
return "application/vnd.rar"
# Audio
elif data.startswith(b"OggS"):
return "application/ogg"
elif data.startswith(b"fLaC"):
return "audio/flac"
elif data.startswith(b"RIFF") and data[8:12] == b"WAVE":
return "audio/wav"
elif data.startswith(b"MThd"):
return "audio/midi"
elif data.startswith(b"ID3") or data[:2] == b"\xFF\xFB":
return "audio/mpeg"
# Video
elif len(data) >= 12 and data[4:8] == b"ftyp":
return "video/mp4"
elif len(data) >= 12 and data.startswith(b"RIFF") and data[8:12] == b"AVI ":
return "video/x-msvideo"
elif data.startswith(b"\x1A\x45\xDF\xA3"):
return "video/webm"
# Text
elif any(tag in header for tag in html_tags):
return "text/html"
elif header.lstrip().startswith(b"{") and data.rstrip().endswith(b"}"):
return "application/json"
elif header.lstrip().startswith(b"<"):
return "application/xml"
elif (
header.startswith(b"/*")
or header.startswith(b"@charset")
or b"{\n" in header
or b"{\r\n" in header
):
return "text/css"
elif (
header.startswith(b"//")
or header.startswith(b"/*")
or b"function " in header
or b"var " in header
or b"let " in header
or b"const " in header
or b"=>" in header
):
return "application/javascript"
elif not data or all(32 <= byte <= 126 or byte in (9, 10, 13) for byte in data):
return "text/plain"
return "application/octet-stream"