diff --git a/internal/mimeutil/detect.go b/internal/mimeutil/detect.go new file mode 100644 index 0000000..da3684d --- /dev/null +++ b/internal/mimeutil/detect.go @@ -0,0 +1,41 @@ +// Package mimeutil 提供 MIME 类型检测工具。 +package mimeutil + +import ( + "mime" + "path/filepath" + "strings" +) + +// mimeOverrides 补充 Go 标准库缺失或错误的 MIME 类型映射。 +// 使用包本地映射而非 mime.AddExtensionType,避免全局副作用。 +// +// 注意: 部分扩展名 Go 返回错误类型而非缺失: +// - .otf: Go 映射到 OpenDocument 公式模板,应为字体格式 +// - .webm: Go 返回 audio/webm,但 webm 可包含视频 +var mimeOverrides = map[string]string{ + ".eot": "application/vnd.ms-fontobject", // 缺失 + ".otf": "font/otf", // Go 返回错误类型 + ".webmanifest": "application/manifest+json", // 缺失 + ".map": "application/json", // 缺失 + ".webm": "video/webm", // Go 返回 audio/webm + // 注意: Go 1.26.2+ 已正确支持 .mjs, .avif, .woff, .woff2 +} + +// DetectContentType 检测文件的 MIME 类型。 +// +// 优先使用包本地映射,回退到 Go 标准库 mime.TypeByExtension。 +// 自动处理扩展名大小写问题。 +// +// 参数: +// - filePath: 文件路径 +// +// 返回值: +// - string: MIME 类型,未知类型返回空字符串 +func DetectContentType(filePath string) string { + ext := strings.ToLower(filepath.Ext(filePath)) + if mime, ok := mimeOverrides[ext]; ok { + return mime + } + return mime.TypeByExtension(ext) +} diff --git a/internal/mimeutil/detect_test.go b/internal/mimeutil/detect_test.go new file mode 100644 index 0000000..8be76ba --- /dev/null +++ b/internal/mimeutil/detect_test.go @@ -0,0 +1,44 @@ +package mimeutil + +import "testing" + +// TestDetectContentType 测试 MIME 类型检测 +func TestDetectContentType(t *testing.T) { + tests := []struct { + filePath string + want string + }{ + // 标准库已知类型 (验证回退) + {"test.html", "text/html; charset=utf-8"}, + {"test.css", "text/css; charset=utf-8"}, + {"test.js", "text/javascript; charset=utf-8"}, + {"test.json", "application/json"}, + {"test.mjs", "text/javascript; charset=utf-8"}, // Go 1.26.2+ 已支持 + + // 包本地补充类型 - 缺失 + {"test.eot", "application/vnd.ms-fontobject"}, + {"test.webmanifest", "application/manifest+json"}, + {"test.map", "application/json"}, + + // 包本地覆盖类型 - Go 返回错误类型 + {"test.otf", "font/otf"}, // Go 返回 OpenDocument 公式模板 + {"test.webm", "video/webm"}, // Go 返回 audio/webm + + // 大小写处理 - 验证 strings.ToLower + {"test.EOT", "application/vnd.ms-fontobject"}, // 不在 Go 表中 + {"test.WEBMANIFEST", "application/manifest+json"}, + {"test.JPG", "image/jpeg"}, // Go 已知,也处理大小写 + + // 未知类型 + {"test.unknown", ""}, + } + + for _, tt := range tests { + t.Run(tt.filePath, func(t *testing.T) { + got := DetectContentType(tt.filePath) + if got != tt.want { + t.Errorf("DetectContentType(%q) = %q, want %q", tt.filePath, got, tt.want) + } + }) + } +}