Python mimetypesの使い方|guess_typeでファイル名からMIMEタイプを判定する方法
Pythonの標準ライブラリmimetypesは、mimetypes.guess_type(“photo.png”)のようにファイル名やURLを渡すと、拡張子からMIMEタイプを推定して(“image/png”, None)のようなタプルで返します。Python 3.13以降は、ファイルパスには新しいguess_file_type()を使うのが公式の推奨です。
この記事では、guess_type()の戻り値の読み方、拡張子だけを渡すと判定されない落とし穴、MIMEタイプから拡張子を引く方法、独自の型の登録、そして「拡張子しか見ない」ことによる限界までを、実際に動かしたコードと出力で説明します。動作確認はPython 3.14.8(macOS)で行い、Python 3.12との違いがある箇所はその都度書いています(2026年10月15日確認)。
MIMEタイプとmimetypesモジュール
MIMEタイプ(メディアタイプ)は、データの種類をimage/pngやtext/htmlのように「タイプ/サブタイプ」で表す識別子です。WebサーバーはレスポンスのContent-Typeヘッダーにこれを入れ、ブラウザはそれを見て画像として表示するか、ダウンロードさせるかを決めます。メールの添付ファイルでも同じ仕組みが使われます。
mimetypesはPythonに最初から入っている標準ライブラリなので、pipでのインストールは不要です。公式ドキュメントの説明どおり、このモジュールはファイル名の拡張子とMIMEタイプの対応表を引くだけで、ファイルの中身は読みません。この性質は後半の「注意点」で詳しく扱います。
| やりたいこと | 使う関数 | 戻り値の例 |
|---|---|---|
| ファイル名・URLからMIMEタイプ | guess_type(url) | ('image/png', None) |
| ファイルパスからMIMEタイプ(3.13以降) | guess_file_type(path) | ('image/png', None) |
| MIMEタイプから拡張子を1つ | guess_extension(type) | '.jpg' |
| MIMEタイプから拡張子を全部 | guess_all_extensions(type) | ['.jpg', '.jpe', '.jpeg'] |
| 対応表に型を追加 | add_type(type, ext) | なし |
guess_type()の基本:ファイル名からMIMEタイプを判定する
まずはよく使う拡張子をまとめて判定してみます。
import mimetypes
for name in ["photo.png", "report.pdf", "app.js", "data.csv", "page.html", "memo.md", "song.mp3", "movie.mp4", "data.json", "PHOTO.JPG", "README", "archive.tar.gz", "image.webp", "sheet.xlsx"]:
print(f"{name:15} -> {mimetypes.guess_type(name)}")
実行結果:
photo.png -> ('image/png', None)
report.pdf -> ('application/pdf', None)
app.js -> ('text/javascript', None)
data.csv -> ('text/csv', None)
page.html -> ('text/html', None)
memo.md -> ('text/markdown', None)
song.mp3 -> ('audio/mpeg', None)
movie.mp4 -> ('video/mp4', None)
data.json -> ('application/json', None)
PHOTO.JPG -> ('image/jpeg', None)
README -> (None, None)
archive.tar.gz -> ('application/x-tar', 'gzip')
image.webp -> ('image/webp', None)
sheet.xlsx -> ('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', None)
戻り値は常に(type, encoding)の2要素のタプルです。読み方のポイントは次の3つです。
- 判定できないときは
(None, None):拡張子のないREADMEのように、対応表にない場合は例外ではなくNoneが返ります。必ずNoneのときの処理を書いておきます。 - 大文字の拡張子も判定される:
PHOTO.JPGもimage/jpegになります。公式ドキュメントによると、型の拡張子はまず大文字小文字を区別して探し、見つからなければ区別せずに探します。 - 2つ目の値は「圧縮形式」:
archive.tar.gzは('application/x-tar', 'gzip')です。中身はtarで、それをgzipで圧縮している、という意味です。この値はContent-Encodingヘッダー向けの値です。
なお、Python 3.9(macOS標準のpython3)で同じコードを動かすと、memo.mdは(None, None)でした。Python 3.12と3.14ではtext/markdownになります。対応表はPythonのバージョンで少しずつ増えているため、古いバージョンでは判定できない拡張子があります。
Python 3.13以降はguess_file_type()を使う
公式ドキュメントでは、Python 3.13でguess_file_type(path)が追加され、guess_type()にファイルパスを渡す使い方は「soft deprecated(非推奨だが削除予定はない)」になりました。今後、ローカルのファイルパスを判定するときはguess_file_type()、URLを判定するときはguess_type()と使い分けます。guess_file_type()はstr・bytes・pathlib.Pathのどれでも受け取れます。
import mimetypes
from pathlib import Path
print(mimetypes.guess_file_type(Path("uploads") / "photo.png"))
print(mimetypes.guess_file_type("backup.tar.gz"))
実行結果:
('image/png', None)
('application/x-tar', 'gzip')
Python 3.12以前で同じコードを動かすとAttributeError: module 'mimetypes' has no attribute 'guess_file_type'になります(Python 3.12.15で確認)。古いバージョンでも動かす必要があるコードでは、従来どおりguess_type()を使ってください。
URLを渡すとき
guess_type()はURLを受け取れ、クエリ文字列(?v=3)は無視してパス部分の拡張子で判定します。拡張子のないURLは判定できません。
import mimetypes
print(mimetypes.guess_type("https://example.com/img/logo.svg?v=3"))
print(mimetypes.guess_type("https://example.com/download"))
実行結果:
('image/svg+xml', None)
(None, None)
落とし穴:拡張子だけを渡すとNoneになる
「拡張子からMIMEタイプを知りたい」ときにguess_type(".html")と書くと、期待に反して(None, None)が返ります。.htmlという文字列は「拡張子のない、ドットで始まる隠しファイル名」として扱われるためです。ネット上にはguess_type(".html")でtext/htmlが返ると説明している記事もありますが、実際に動かすとそうはなりません。
import mimetypes
ext = ".html"
print(mimetypes.guess_type(ext)) # 拡張子だけだと判定されない
print(mimetypes.guess_type("file" + ext)) # ダミーの名前を付ける
print(mimetypes.types_map[ext]) # 辞書から直接引く
実行結果:
(None, None)
('text/html', None)
text/html
拡張子だけを持っている場合は、"file" + extのようにダミーの名前を付けて渡すか、対応表の辞書mimetypes.types_mapを直接引きます。types_map[ext]は存在しない拡張子でKeyErrorになるので、実務ではtypes_map.get(ext)にしておくと安全です。
MIMEタイプから拡張子を取得する
逆方向の変換にはguess_extension()とguess_all_extensions()を使います。ダウンロードしたデータを保存するときに、Content-Typeから保存用の拡張子を決める場面で使います。
import mimetypes
print(mimetypes.guess_extension("image/jpeg"))
print(mimetypes.guess_extension("text/plain"))
print(mimetypes.guess_all_extensions("image/jpeg"))
print(mimetypes.guess_all_extensions("text/plain"))
print(mimetypes.guess_extension("application/x-unknown"))
実行結果:
.jpg
.txt
['.jpg', '.jpe', '.jpeg']
['.txt', '.bat', '.c', '.h', '.ksh', '.pl', '.srt', '.text', '.conf', '.def', '.list', '.log', '.in']
None
image/jpegには.jpg・.jpe・.jpegの3つが対応しており、guess_extension()はそのうち.jpgを返しました(Python 3.12と3.14で同じ結果)。text/plainのように対応する拡張子が多い型では、guess_all_extensions()の結果が長くなります。対応する拡張子がない型ではNoneです。
独自のMIMEタイプを追加する:add_type()とMimeTypes
対応表にない拡張子はadd_type()で登録できます。次の例では、手元の環境で判定できなかった.tomlを登録しています。
import mimetypes
print(mimetypes.guess_type("setting.toml"))
mimetypes.add_type("application/toml", ".toml")
print(mimetypes.guess_type("setting.toml"))
実行結果:
(None, None)
('application/toml', None)
add_type()はモジュール全体の対応表を書き換えるため、同じプロセス内のほかの処理にも影響します。影響を閉じ込めたいときは、MimeTypesクラスで独立した対応表を作ります。
import mimetypes
db = mimetypes.MimeTypes()
db.add_type("application/x-techgym-note", ".tgn")
print(db.guess_type("lesson.tgn"))
print(mimetypes.guess_type("lesson.tgn"))
実行結果:
('application/x-techgym-note', None)
(None, None)
結果はOSとstrict引数で変わる
mimetypesは、Python内蔵の対応表に加えて、OSにあるmime.typesファイル(/etc/mime.typesや/etc/apache2/mime.typesなど、mimetypes.knownfilesに並ぶパス)を読み込みます。Windowsではレジストリの設定も読み込みます。つまり同じコードでもOSによって結果が変わることがあります。
次のコードは、標準の関数(OSのファイルも読む)と、MimeTypes()で作ったPython内蔵の表だけを使うオブジェクトの結果を比べたものです。
import mimetypes
builtin = mimetypes.MimeTypes() # Python内蔵の表だけを使う
for name in ["old.pict", "chart.xyz", "app.apk"]:
print(f"{name:10} 標準={mimetypes.guess_type(name)[0]} 内蔵のみ={builtin.guess_type(name)[0]} 内蔵(strict=False)={builtin.guess_type(name, strict=False)[0]}")
実行結果:
old.pict 標準=image/pict 内蔵のみ=None 内蔵(strict=False)=image/pict
chart.xyz 標準=chemical/x-xyz 内蔵のみ=None 内蔵(strict=False)=None
app.apk 標準=application/vnd.android.package-archive 内蔵のみ=None 内蔵(strict=False)=application/vnd.android.package-archive
macOSでは/etc/apache2/mime.typesに定義があるため、.xyzまで判定できました。Python内蔵の表だけでは判定できません。またstrict=Falseを付けると、IANAに登録された正式な型に加えて「非標準だがよく使われる型」も探します。.apkはPython 3.14ではstrict=Falseで判定できましたが、Python 3.12ではNoneでした。サーバーとローカルPCで結果が違うときは、OSの設定ファイルとPythonのバージョンを疑ってください。
実用例:Content-Typeヘッダーを決める
Webアプリでファイルを返すときのContent-Typeを決める関数の例です。判定できないファイルと圧縮ファイルは、汎用のapplication/octet-streamにしています。
import mimetypes
def content_type(filename):
mime, encoding = mimetypes.guess_file_type(filename)
if mime is None or encoding is not None:
return "application/octet-stream"
if mime.startswith("text/"):
return f"{mime}; charset=utf-8"
return mime
for name in ["index.html", "logo.png", "notes.txt", "backup.tar.gz", "README"]:
print(f"{name:14} -> {content_type(name)}")
実行結果:
index.html -> text/html; charset=utf-8
logo.png -> image/png
notes.txt -> text/plain; charset=utf-8
backup.tar.gz -> application/octet-stream
README -> application/octet-stream
テキスト系の型にcharsetを付けているのは、文字コードを指定しないと日本語が文字化けすることがあるためです。.tar.gzをapplication/x-tarで返すと、ブラウザが圧縮されたままのデータをtarとして扱ってしまうので、ここでは圧縮されているものをまとめてapplication/octet-streamにしています。
コマンドラインからも使える
python -m mimetypesで、プログラムを書かずに判定できます。-e(--extension)を付けるとMIMEタイプから拡張子を引き、-l(--lenient)を付けると非標準の型も探します(公式ドキュメントのCommand-line usage)。
$ python3.14 -m mimetypes photo.png archive.tar.gz
type: image/png encoding: None
type: application/x-tar encoding: gzip
$ python3.14 -m mimetypes -e image/png
.png
注意点:mimetypesはファイルの中身を見ない
mimetypesが見るのは名前だけです。拡張子を.pngに変えたPythonスクリプトもimage/pngと判定されます。アップロードされたファイルの種類チェックをmimetypesだけで行うと、偽装したファイルを通してしまいます。
次の例は、ファイル先頭の数バイト(マジックナンバー)と拡張子の判定を突き合わせる簡単なチェックです。
import mimetypes
from pathlib import Path
SIGNATURES = {
b"\x89PNG\r\n\x1a\n": "image/png",
b"\xff\xd8\xff": "image/jpeg",
b"%PDF-": "application/pdf",
}
def sniff(path):
head = Path(path).read_bytes()[:8]
for magic, mime in SIGNATURES.items():
if head.startswith(magic):
return mime
return None
Path("fake.png").write_text("print('hello')")
Path("real.png").write_bytes(b"\x89PNG\r\n\x1a\n" + b"\x00" * 16)
for name in ["fake.png", "real.png"]:
by_name, _ = mimetypes.guess_file_type(name)
by_bytes = sniff(name)
ok = "OK" if by_name == by_bytes else "NG"
print(f"{name}: 拡張子={by_name} 中身={by_bytes} -> {ok}")
実行結果:
fake.png: 拡張子=image/png 中身=None -> NG
real.png: 拡張子=image/png 中身=image/png -> OK
PNGは\x89PNG\r\n\x1a\n、PDFは%PDF-で始まるといった決まりを利用しています。実際のアプリでは、扱うファイル形式ごとにライブラリで実際に開けるかを確かめる(画像ならPillowで読み込む、など)のが確実です。mimetypesは「名前から型を決める」道具、中身の検証は別の仕組み、と分けて考えてください。
関連する標準ライブラリ
mimetypesは、メールに添付ファイルを付けるときにも使います。添付する前にMIMEタイプを決め、EmailMessage.add_attachment()にmaintypeとsubtypeを渡す流れです。メールの組み立てはPython emailとは?EmailMessageでメールを組み立てる方法、送信はPython smtplibとは?メールを送信する方法で解説しています。
よくある質問
Q. mimetypesはインストールが必要ですか?
不要です。mimetypesはPythonの標準ライブラリなので、import mimetypesと書くだけで使えます。
Q. guess_type()とguess_file_type()はどちらを使えばよいですか?
Python 3.13以降でローカルのファイルパスを判定するならguess_file_type()、URLを判定するならguess_type()です。Python 3.12以前にはguess_file_type()がないため、guess_type()を使います。
Q. mimetypes.guess_type(“.html”)がNoneを返すのはなぜですか?
“.html”は拡張子のない隠しファイル名として扱われるためです。”file.html”のように名前を付けて渡すか、mimetypes.types_map.get(“.html”)で辞書から直接引いてください。
Q. ファイルの中身から種類を判定できますか?
mimetypesはファイル名の拡張子しか見ないので、中身は判定できません。アップロードされたファイルの検証には、先頭のバイト列の確認や、画像ならPillowで実際に開けるかの確認を組み合わせてください。
Q. 環境によって結果が違うのはなぜですか?
mimetypesはPython内蔵の対応表に加えて、OSのmime.typesファイルやWindowsのレジストリを読み込むためです。Pythonのバージョンによっても対応表が違います。結果をそろえたいときはMimeTypes()で作った独立した対応表を使い、必要な型をadd_type()で登録します。
あわせて読みたい
学習の順序はPython学習のロードマップ、標準で使える機能の一覧はPython標準ライブラリのモジュール一覧にまとめています。
Pythonの関数・メソッドの全体像はPython基本関数・メソッド完全一覧にまとめています。







