using System; using System.Collections.Generic; using System.IO; using System.Linq; namespace Nuuru.Server.Utilities { public static class MIME { private static readonly Dictionary ExtensionToMime = new Dictionary(StringComparer.OrdinalIgnoreCase) { { ".jpg", "image/jpeg" }, { ".jpeg", "image/jpeg" }, { ".png", "image/png" }, { ".gif", "image/gif" }, { ".bmp", "image/bmp" }, { ".webp", "image/webp" }, { ".svg", "image/svg+xml" }, { ".ico", "image/x-icon" }, { ".mp4", "video/mp4" }, { ".avi", "video/x-msvideo" }, { ".webm", "video/webm" }, { ".mkv", "video/x-matroska" }, { ".mov", "video/quicktime" }, { ".wmv", "video/x-ms-wmv" }, { ".flv", "video/x-flv" }, { ".mp3", "audio/mpeg" }, { ".wav", "audio/wav" }, { ".ogg", "audio/ogg" }, { ".m4a", "audio/mp4" }, { ".flac", "audio/flac" }, { ".aac", "audio/aac" }, { ".wma", "audio/x-ms-wma" }, { ".swf", "application/x-shockwave-flash" }, { ".pdf", "application/pdf" }, { ".zip", "application/zip" }, { ".rar", "application/vnd.rar" }, { ".7z", "application/x-7z-compressed" }, { ".tar", "application/x-tar" }, { ".gz", "application/gzip" }, { ".json", "application/json" }, { ".xml", "application/xml" }, { ".html", "text/html" }, { ".htm", "text/html" }, { ".css", "text/css" }, { ".js", "text/javascript" }, { ".txt", "text/plain" }, { ".csv", "text/csv" }, { ".md", "text/markdown" }, { ".doc", "application/msword" }, { ".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document" }, { ".xls", "application/vnd.ms-excel" }, { ".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" }, { ".ppt", "application/vnd.ms-powerpoint" }, { ".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation" }, { ".exe", "application/vnd.microsoft.portable-executable" }, { ".dll", "application/vnd.microsoft.portable-executable" }, { ".ttf", "font/ttf" }, { ".otf", "font/otf" }, { ".woff", "font/woff" }, { ".woff2", "font/woff2" } }; private static readonly Dictionary MimeToExtension = new Dictionary(StringComparer.OrdinalIgnoreCase) { { "image/jpeg", ".jpg" }, { "image/png", ".png" }, { "image/gif", ".gif" }, { "image/bmp", ".bmp" }, { "image/webp", ".webp" }, { "image/svg+xml", ".svg" }, { "image/x-icon", ".ico" }, { "video/mp4", ".mp4" }, { "video/x-msvideo", ".avi" }, { "video/webm", ".webm" }, { "video/x-matroska", ".mkv" }, { "video/quicktime", ".mov" }, { "video/x-ms-wmv", ".wmv" }, { "video/x-flv", ".flv" }, { "audio/mpeg", ".mp3" }, { "audio/wav", ".wav" }, { "audio/ogg", ".ogg" }, { "audio/mp4", ".m4a" }, { "audio/flac", ".flac" }, { "audio/aac", ".aac" }, { "audio/x-ms-wma", ".wma" }, { "application/x-shockwave-flash", ".swf" }, { "application/pdf", ".pdf" }, { "application/zip", ".zip" }, { "application/vnd.rar", ".rar" }, { "application/x-7z-compressed", ".7z" }, { "application/x-tar", ".tar" }, { "application/gzip", ".gz" }, { "application/json", ".json" }, { "application/xml", ".xml" }, { "text/html", ".html" }, { "text/css", ".css" }, { "text/javascript", ".js" }, { "text/plain", ".txt" }, { "text/csv", ".csv" }, { "text/markdown", ".md" }, { "application/msword", ".doc" }, { "application/vnd.openxmlformats-officedocument.wordprocessingml.document", ".docx" }, { "application/vnd.ms-excel", ".xls" }, { "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", ".xlsx" }, { "application/vnd.ms-powerpoint", ".ppt" }, { "application/vnd.openxmlformats-officedocument.presentationml.presentation", ".pptx" }, { "application/vnd.microsoft.portable-executable", ".exe" }, { "font/ttf", ".ttf" }, { "font/otf", ".otf" }, { "font/woff", ".woff" }, { "font/woff2", ".woff2" }, { "application/octet-stream", ".bin" } }; public static string GetExtension(string mimeType) { if (string.IsNullOrEmpty(mimeType)) return ".bin"; return MimeToExtension.TryGetValue(mimeType, out string extension) ? extension : ".bin"; } public static string DetectMIME(Stream stream) { if (stream == null) return "application/octet-stream"; byte[] header = new byte[16]; int bytesRead; if (stream.CanSeek) { long originalPos = stream.Position; bytesRead = ReadUpTo(stream, header, header.Length); stream.Position = originalPos; } else { bytesRead = ReadUpTo(stream, header, header.Length); } return DetectMIME(header.AsSpan(0, bytesRead)); } public static string DetectMIME(byte[] data) { if (data == null || data.Length == 0) return "application/octet-stream"; return DetectMIME(new ReadOnlySpan(data, 0, data.Length)); } public static string DetectMIMEByExtension(string fileName) { if (string.IsNullOrEmpty(fileName)) return "application/octet-stream"; string extension = Path.GetExtension(fileName); if (string.IsNullOrEmpty(extension)) return "application/octet-stream"; return ExtensionToMime.TryGetValue(extension, out string mimeType) ? mimeType : "application/octet-stream"; } public static string DetectMIME(Stream stream, string fileName) { string contentMime = DetectMIME(stream); if (contentMime != "application/octet-stream") return contentMime; return DetectMIMEByExtension(fileName); } public static string DetectMIME(byte[] data, string fileName) { string contentMime = DetectMIME(data); if (contentMime != "application/octet-stream") return contentMime; return DetectMIMEByExtension(fileName); } private static string DetectMIME(ReadOnlySpan data) { const string defaultMime = "application/octet-stream"; if (data.Length >= 3) { if ((data[0] == 0x46 || data[0] == 0x43 || data[0] == 0x5A) && data[1] == 0x57 && data[2] == 0x53) return "application/x-shockwave-flash"; if (data[0] == 0x47 && data[1] == 0x49 && data[2] == 0x46) return "image/gif"; } if (data.Length >= 2) { if (data[0] == 0xFF && data[1] == 0xD8) return "image/jpeg"; } if (data.Length >= 8) { if (data[0] == 0x89 && data[1] == 0x50 && data[2] == 0x4E && data[3] == 0x47 && data[4] == 0x0D && data[5] == 0x0A && data[6] == 0x1A && data[7] == 0x0A) return "image/png"; } if (data.Length >= 12) { if (data[4] == 0x66 && data[5] == 0x74 && data[6] == 0x79 && data[7] == 0x70) return "video/mp4"; if (data[0] == 0x52 && data[1] == 0x49 && data[2] == 0x46 && data[3] == 0x46) { if (data[8] == 0x41 && data[9] == 0x56 && data[10] == 0x49 && data[11] == 0x20) return "video/x-msvideo"; if (data[8] == 0x57 && data[9] == 0x41 && data[10] == 0x56 && data[11] == 0x45) return "audio/wav"; } } if (data.Length >= 4) { if (data[0] == 0x1A && data[1] == 0x45 && data[2] == 0xDF && data[3] == 0xA3) return "video/webm"; } return defaultMime; } private static int ReadUpTo(Stream stream, byte[] buffer, int count) { int total = 0; while (total < count) { int read = stream.Read(buffer, total, count - total); if (read <= 0) break; total += read; } return total; } } }