File extensions and MIME Types in .NET
I want to get a MIME Content-Type from a given extension (preferably without accessing the physical file). I have seen some questions about this and the methods described to perform this can be resumed in:
I've been using no.1 for some time but I realized that the information provided by the registry is not consistent and depends on the software installed on the machine. Some extensions, like .zip don't use to have a Content-Type specified.
Solution no.2 forces me to have the file on disk in order to read the first bytes, which is something slow but may get good results.
The third method is based on Directory Services and all that stuff, which is something I don't like much because I have to add COM references and I'm not sure it's consistent between IIS6 and IIS7. Also, I don't know the performance of this method.
Finally, I didn't want to use my own table but at the end seems the best option if I want a decent performance and consistency of the results between platforms (even Mono).
Do you think there's a better option than using my own table or one of other described methods are better? What's your experience?
It depends what you need the MIME type for. In general, for services (web apps, web service, etc.), it's advisable not to use a MIME list which is dependent on the OS settings, or only as fallback if you cannot find MIME information otherwise.
I think that this is also the reason why MS chose to put constant MIME types in their System.Web.MimeMapping
class (unfortunately it's internal, for whatever reason).
Edit:
Wrapper (<= .NET 3.5)
public static class MimeExtensionHelper
{
static object locker = new object();
static object mimeMapping;
static MethodInfo getMimeMappingMethodInfo;
static MimeExtensionHelper()
{
Type mimeMappingType = Assembly.GetAssembly(typeof(HttpRuntime)).GetType("System.Web.MimeMapping");
if (mimeMappingType == null)
throw new SystemException("Couldnt find MimeMapping type");
ConstructorInfo constructorInfo = mimeMappingType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, null);
if (constructorInfo == null)
throw new SystemException("Couldnt find default constructor for MimeMapping");
mimeMapping = constructorInfo.Invoke(null);
if (mimeMapping == null)
throw new SystemException("Couldnt find MimeMapping");
getMimeMappingMethodInfo = mimeMappingType.GetMethod("GetMimeMapping", BindingFlags.Static | BindingFlags.NonPublic);
if (getMimeMappingMethodInfo == null)
throw new SystemException("Couldnt find GetMimeMapping method");
if (getMimeMappingMethodInfo.ReturnType != typeof(string))
throw new SystemException("GetMimeMapping method has invalid return type");
if (getMimeMappingMethodInfo.GetParameters().Length != 1 && getMimeMappingMethodInfo.GetParameters()[0].ParameterType != typeof(string))
throw new SystemException("GetMimeMapping method has invalid parameters");
}
public static string GetMimeType(string filename)
{
lock (locker)
return (string)getMimeMappingMethodInfo.Invoke(mimeMapping, new object[] { filename });
}
}
Wrapper (.NET 4.0)
public static class MimeExtensionHelper
{
static object locker = new object();
static object mimeMapping;
static MethodInfo getMimeMappingMethodInfo;
static MimeExtensionHelper()
{
Type mimeMappingType = Assembly.GetAssembly(typeof(HttpRuntime)).GetType("System.Web.MimeMapping");
if (mimeMappingType == null)
throw new SystemException("Couldnt find MimeMapping type");
getMimeMappingMethodInfo = mimeMappingType.GetMethod("GetMimeMapping", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
if (getMimeMappingMethodInfo == null)
throw new SystemException("Couldnt find GetMimeMapping method");
if (getMimeMappingMethodInfo.ReturnType != typeof(string))
throw new SystemException("GetMimeMapping method has invalid return type");
if (getMimeMappingMethodInfo.GetParameters().Length != 1 && getMimeMappingMethodInfo.GetParameters()[0].ParameterType != typeof(string))
throw new SystemException("GetMimeMapping method has invalid parameters");
}
public static string GetMimeType(string filename)
{
lock (locker)
return (string)getMimeMappingMethodInfo.Invoke(mimeMapping, new object[] { filename });
}
}
.NET 4.5+
No wrapper required, call the public method System.Web.MimeMapping.GetMimeMapping
directly.
I have combined all these approaches in my utility lib, except maybe no.3. Btw, no.2 (urlmon.dll) doesn't require static file, it simply takes some bytes no matter where they had come from. Here's my current class
namespace Components
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Xml.Serialization;
using Microsoft.Win32;
public sealed class MimeExtensionHelper
{
private MimeExtensionHelper() { }
/// <summary>Finds extension associated with specified mime type</summary>
/// <param name="mimeType">mime type you search extension for, e.g.: "application/octet-stream"</param>
/// <returns>most used extension, associated with provided type, e.g.: ".bin"</returns>
public static string FindExtension(string mimeType)
{
return ExtensionTypes.GetExtension(mimeType);
}
/// <summary>Finds mime type using provided extension and/or file's binary content.</summary>
/// <param name="file">Full file path</param>
/// <param name="verifyFromContent">Should the file's content be examined to verify founded value.</param>
/// <returns>mime type of file, e.g.: "application/octet-stream"</returns>
public static string FindMime(string file,bool verifyFromContent)
{
string extension = Path.GetExtension(file);
string mimeType = string.Empty;
try
{
if (!String.IsNullOrEmpty(extension))
mimeType = ExtensionTypes.GetMimeType(extension);
if (verifyFromContent
|| (String.IsNullOrEmpty(mimeType) && File.Exists(file)))
mimeType = FindMimeByContent(file,mimeType);
}
catch { }
return (mimeType ?? string.Empty).Trim();//"application/octet-stream"
}
/// <summary>Finds mime type for file using it's binary data.</summary>
/// <param name="file">Full path to file.</param>
/// <param name="proposedType">Optional. Expected file's type.</param>
/// <returns>mime type, e.g.: "application/octet-stream"</returns>
public static string FindMimeByContent(string file
,string proposedType)
{
FileInfo fi = new FileInfo(file);
if (!fi.Exists)
throw new FileNotFoundException(file);
byte[] buf = new byte[Math.Min(4096L,fi.Length)];
using (FileStream fs = File.OpenRead(file))
fs.Read(buf,0,buf.Length);
return FindMimeByData(buf,proposedType);
}
/// <summary>Finds mime type for binary data.</summary>
/// <param name="dataBytes">Binary data to examine.</param>
/// <param name="mimeProposed">Optional. Expected mime type.</param>
/// <returns>mime type, e.g.: "application/octet-stream"</returns>
public static string FindMimeByData(byte[] dataBytes,string mimeProposed)
{
if (dataBytes == null || dataBytes.Length == 0)
throw new ArgumentNullException("dataBytes");
string mimeRet = String.Empty;
IntPtr outPtr = IntPtr.Zero;
if (!String.IsNullOrEmpty(mimeProposed))
mimeRet = mimeProposed;
int result = FindMimeFromData(IntPtr.Zero
,null
,dataBytes
,dataBytes.Length
,String.IsNullOrEmpty(mimeProposed) ? null : mimeProposed
,0
,out outPtr
,0);
if (result != 0)
throw Marshal.GetExceptionForHR(result);
if (outPtr != null && outPtr != IntPtr.Zero)
{
mimeRet = Marshal.PtrToStringUni(outPtr);
Marshal.FreeCoTaskMem(outPtr);
}
return mimeRet;
}
[DllImport("urlmon.dll"
,CharSet = CharSet.Unicode
,ExactSpelling = true
,SetLastError = true)]
static extern Int32 FindMimeFromData(IntPtr pBC
,[MarshalAs(UnmanagedType.LPWStr)] String pwzUrl
,[MarshalAs(UnmanagedType.LPArray,ArraySubType = UnmanagedType.I1,SizeParamIndex = 3)] Byte[] pBuffer
,Int32 cbSize
,[MarshalAs(UnmanagedType.LPWStr)] String pwzMimeProposed
,Int32 dwMimeFlags
,out IntPtr ppwzMimeOut
,Int32 dwReserved);
private static MimeTypeCollection _extensionTypes = null;
private static MimeTypeCollection ExtensionTypes
{
get
{
if (_extensionTypes == null)
_extensionTypes = new MimeTypeCollection();
return _extensionTypes;
}
}
[Serializable]
[XmlRoot(ElementName = "mimeTypes")]
private class MimeTypeCollection : List<MimeTypeCollection.mimeTypeInfo>
{
private SortedList<string,string> _extensions;
private SortedList<string,List<string>> _mimes;
private void Init()
{
if (_extensions == null || _mimes == null
|| _extensions.Count == 0 || _mimes.Count == 0)
{
_extensions = new SortedList<string,string>(StringComparer.OrdinalIgnoreCase);
_mimes = new SortedList<string,List<string>>(StringComparer.OrdinalIgnoreCase);
foreach (var mime in this)
{
_mimes.Add(mime.MimeType,new List<string>(mime.Extensions));
foreach (string ext in mime.Extensions)
if (!_extensions.ContainsKey(ext))
_extensions.Add(ext,mime.MimeType);
}
}
}
public String GetExtension(string type)
{
Init();
return _mimes.ContainsKey(type) ? _mimes[type][0] : string.Empty;
}
public String GetMimeType(string extension)
{
Init();
return _extensions.ContainsKey(extension) ? _extensions[extension] : string.Empty;
}
public MimeTypeCollection()
{
this.Add(new mimeTypeInfo("application/applixware",new List<string>(new[] { ".aw" })));
this.Add(new mimeTypeInfo("application/atom+xml",new List<string>(new[] { ".atom" })));
// ... Whole list from apache's site
this.Add(new mimeTypeInfo("x-x509-ca-cert",new List<string>(new[] { ".cer" })));
try
{
using (RegistryKey classesRoot = Registry.ClassesRoot)
using (RegistryKey typeKey = classesRoot.OpenSubKey(@"MIMEDatabaseContent Type"))
{
string[] subKeyNames = typeKey.GetSubKeyNames();
string extension = string.Empty;
foreach (string keyname in subKeyNames)
{
string trimmed = (keyname ?? string.Empty).Trim();
if (string.IsNullOrEmpty(trimmed))
continue;
if (!String.IsNullOrEmpty(GetExtension(trimmed)))
continue;
string subKey = "MIMEDatabaseContent Type" + trimmed;
using (RegistryKey curKey = classesRoot.OpenSubKey(subKey))
{
extension = (curKey.GetValue("Extension") as string ?? string.Empty).Trim();
if (extension.Length > 0)
this.Add(new mimeTypeInfo(trimmed
,new List<string>(new[] { extension })));
}
}
}
}
catch (Exception ex)
{
string s = ex.ToString();
}
}
[Serializable]
public class mimeTypeInfo
{
[XmlAttribute(AttributeName = "mimeType")]
public String MimeType { get; set; }
[XmlElement("extension")]
public List<String> Extensions { get; set; }
public mimeTypeInfo(string mimeType,List<string> extensions)
{
MimeType = mimeType;
Extensions = extensions;
}
public mimeTypeInfo() { }
}
}
}
}
The System.Web.MimeMapping
has 3 versions - two version 4.0s (where one of these is internal), and then a version 2.0 which is also internal. As pointed out, there is a public version of the class in System.Web version 4.0 for the .NET 4.5 framework.
For RoadkillWiki I've basically reverse engineered it to save the bother of reflecting each time, the Wiki's file handler tries to the use IIS/applicationhost.config by default, and then falls through to the MimeMapping class:
private string GetMimeType(string fileExtension, ServerManager serverManager)
{
try
{
string mimeType = "text/plain";
Microsoft.Web.Administration.Configuration config = serverManager.GetApplicationHostConfiguration();
ConfigurationSection staticContentSection = config.GetSection("system.webServer/staticContent");
ConfigurationElementCollection mimemaps = staticContentSection.GetCollection();
ConfigurationElement element = mimemaps.FirstOrDefault(m => m.Attributes["fileExtension"].Value.ToString() == fileExtension);
if (element != null)
mimeType = element.Attributes["mimeType"].Value.ToString();
return mimeType;
}
catch (UnauthorizedAccessException)
{
// Shared hosting won't have access to the applicationhost.config file
return MimeMapping.GetMimeMapping("." +fileExtension);
}
}
And MimeMapping:
public class MimeMapping
{
private static Dictionary<string, string> ExtensionMap = new Dictionary<string, string>();
static MimeMapping()
{
ExtensionMap.Add(".323", "text/h323");
ExtensionMap.Add(".asx", "video/x-ms-asf");
ExtensionMap.Add(".acx", "application/internet-property-stream");
ExtensionMap.Add(".ai", "application/postscript");
ExtensionMap.Add(".aif", "audio/x-aiff");
ExtensionMap.Add(".aiff", "audio/aiff");
ExtensionMap.Add(".axs", "application/olescript");
ExtensionMap.Add(".aifc", "audio/aiff");
ExtensionMap.Add(".asr", "video/x-ms-asf");
ExtensionMap.Add(".avi", "video/x-msvideo");
ExtensionMap.Add(".asf", "video/x-ms-asf");
ExtensionMap.Add(".au", "audio/basic");
ExtensionMap.Add(".application", "application/x-ms-application");
ExtensionMap.Add(".bin", "application/octet-stream");
ExtensionMap.Add(".bas", "text/plain");
ExtensionMap.Add(".bcpio", "application/x-bcpio");
ExtensionMap.Add(".bmp", "image/bmp");
ExtensionMap.Add(".cdf", "application/x-cdf");
ExtensionMap.Add(".cat", "application/vndms-pkiseccat");
ExtensionMap.Add(".crt", "application/x-x509-ca-cert");
ExtensionMap.Add(".c", "text/plain");
ExtensionMap.Add(".css", "text/css");
ExtensionMap.Add(".cer", "application/x-x509-ca-cert");
ExtensionMap.Add(".crl", "application/pkix-crl");
ExtensionMap.Add(".cmx", "image/x-cmx");
ExtensionMap.Add(".csh", "application/x-csh");
ExtensionMap.Add(".cod", "image/cis-cod");
ExtensionMap.Add(".cpio", "application/x-cpio");
ExtensionMap.Add(".clp", "application/x-msclip");
ExtensionMap.Add(".crd", "application/x-mscardfile");
ExtensionMap.Add(".deploy", "application/octet-stream");
ExtensionMap.Add(".dll", "application/x-msdownload");
ExtensionMap.Add(".dot", "application/msword");
ExtensionMap.Add(".doc", "application/msword");
ExtensionMap.Add(".dvi", "application/x-dvi");
ExtensionMap.Add(".dir", "application/x-director");
ExtensionMap.Add(".dxr", "application/x-director");
ExtensionMap.Add(".der", "application/x-x509-ca-cert");
ExtensionMap.Add(".dib", "image/bmp");
ExtensionMap.Add(".dcr", "application/x-director");
ExtensionMap.Add(".disco", "text/xml");
ExtensionMap.Add(".exe", "application/octet-stream");
ExtensionMap.Add(".etx", "text/x-setext");
ExtensionMap.Add(".evy", "application/envoy");
ExtensionMap.Add(".eml", "message/rfc822");
ExtensionMap.Add(".eps", "application/postscript");
ExtensionMap.Add(".flr", "x-world/x-vrml");
ExtensionMap.Add(".fif", "application/fractals");
ExtensionMap.Add(".gtar", "application/x-gtar");
ExtensionMap.Add(".gif", "image/gif");
ExtensionMap.Add(".gz", "application/x-gzip");
ExtensionMap.Add(".hta", "application/hta");
ExtensionMap.Add(".htc", "text/x-component");
ExtensionMap.Add(".htt", "text/webviewhtml");
ExtensionMap.Add(".h", "text/plain");
ExtensionMap.Add(".hdf", "application/x-hdf");
ExtensionMap.Add(".hlp", "application/winhlp");
ExtensionMap.Add(".html", "text/html");
ExtensionMap.Add(".htm", "text/html");
ExtensionMap.Add(".hqx", "application/mac-binhex40");
ExtensionMap.Add(".isp", "application/x-internet-signup");
ExtensionMap.Add(".iii", "application/x-iphone");
ExtensionMap.Add(".ief", "image/ief");
ExtensionMap.Add(".ivf", "video/x-ivf");
ExtensionMap.Add(".ins", "application/x-internet-signup");
ExtensionMap.Add(".ico", "image/x-icon");
ExtensionMap.Add(".jpg", "image/jpeg");
ExtensionMap.Add(".jfif", "image/pjpeg");
ExtensionMap.Add(".jpe", "image/jpeg");
ExtensionMap.Add(".jpeg", "image/jpeg");
ExtensionMap.Add(".js", "application/x-javascript");
ExtensionMap.Add(".lsx", "video/x-la-asf");
ExtensionMap.Add(".latex", "application/x-latex");
ExtensionMap.Add(".lsf", "video/x-la-asf");
ExtensionMap.Add(".manifest", "application/x-ms-manifest");
ExtensionMap.Add(".mhtml", "message/rfc822");
ExtensionMap.Add(".mny", "application/x-msmoney");
ExtensionMap.Add(".mht", "message/rfc822");
ExtensionMap.Add(".mid", "audio/mid");
ExtensionMap.Add(".mpv2", "video/mpeg");
ExtensionMap.Add(".man", "application/x-troff-man");
ExtensionMap.Add(".mvb", "application/x-msmediaview");
ExtensionMap.Add(".mpeg", "video/mpeg");
ExtensionMap.Add(".m3u", "audio/x-mpegurl");
ExtensionMap.Add(".mdb", "application/x-msaccess");
ExtensionMap.Add(".mpp", "application/vnd.ms-project");
ExtensionMap.Add(".m1v", "video/mpeg");
ExtensionMap.Add(".mpa", "video/mpeg");
ExtensionMap.Add(".me", "application/x-troff-me");
ExtensionMap.Add(".m13", "application/x-msmediaview");
ExtensionMap.Add(".movie", "video/x-sgi-movie");
ExtensionMap.Add(".m14", "application/x-msmediaview");
ExtensionMap.Add(".mpe", "video/mpeg");
ExtensionMap.Add(".mp2", "video/mpeg");
ExtensionMap.Add(".mov", "video/quicktime");
ExtensionMap.Add(".mp3", "audio/mpeg");
ExtensionMap.Add(".mpg", "video/mpeg");
ExtensionMap.Add(".ms", "application/x-troff-ms");
ExtensionMap.Add(".nc", "application/x-netcdf");
ExtensionMap.Add(".nws", "message/rfc822");
ExtensionMap.Add(".oda", "application/oda");
ExtensionMap.Add(".ods", "application/oleobject");
ExtensionMap.Add(".pmc", "application/x-perfmon");
ExtensionMap.Add(".p7r", "application/x-pkcs7-certreqresp");
ExtensionMap.Add(".p7b", "application/x-pkcs7-certificates");
ExtensionMap.Add(".p7s", "application/pkcs7-signature");
ExtensionMap.Add(".pmw", "application/x-perfmon");
ExtensionMap.Add(".ps", "application/postscript");
ExtensionMap.Add(".p7c", "application/pkcs7-mime");
ExtensionMap.Add(".pbm", "image/x-portable-bitmap");
ExtensionMap.Add(".ppm", "image/x-portable-pixmap");
ExtensionMap.Add(".pub", "application/x-mspublisher");
ExtensionMap.Add(".pnm", "image/x-portable-anymap");
ExtensionMap.Add(".pml", "application/x-perfmon");
ExtensionMap.Add(".p10", "application/pkcs10");
ExtensionMap.Add(".pfx", "application/x-pkcs12");
ExtensionMap.Add(".p12", "application/x-pkcs12");
ExtensionMap.Add(".pdf", "application/pdf");
ExtensionMap.Add(".pps", "application/vnd.ms-powerpoint");
ExtensionMap.Add(".p7m", "application/pkcs7-mime");
ExtensionMap.Add(".pko", "application/vndms-pkipko");
ExtensionMap.Add(".ppt", "application/vnd.ms-powerpoint");
ExtensionMap.Add(".pmr", "application/x-perfmon");
ExtensionMap.Add(".pma", "application/x-perfmon");
ExtensionMap.Add(".pot", "application/vnd.ms-powerpoint");
ExtensionMap.Add(".prf", "application/pics-rules");
ExtensionMap.Add(".pgm", "image/x-portable-graymap");
ExtensionMap.Add(".qt", "video/quicktime");
ExtensionMap.Add(".ra", "audio/x-pn-realaudio");
ExtensionMap.Add(".rgb", "image/x-rgb");
ExtensionMap.Add(".ram", "audio/x-pn-realaudio");
ExtensionMap.Add(".rmi", "audio/mid");
ExtensionMap.Add(".ras", "image/x-cmu-raster");
ExtensionMap.Add(".roff", "application/x-troff");
ExtensionMap.Add(".rtf", "application/rtf");
ExtensionMap.Add(".rtx", "text/richtext");
ExtensionMap.Add(".sv4crc", "application/x-sv4crc");
ExtensionMap.Add(".spc", "application/x-pkcs7-certificates");
ExtensionMap.Add(".setreg", "application/set-registration-initiation");
ExtensionMap.Add(".snd", "audio/basic");
ExtensionMap.Add(".stl", "application/vndms-pkistl");
ExtensionMap.Add(".setpay", "application/set-payment-initiation");
ExtensionMap.Add(".stm", "text/html");
ExtensionMap.Add(".shar", "application/x-shar");
ExtensionMap.Add(".sh", "application/x-sh");
ExtensionMap.Add(".sit", "application/x-stuffit");
ExtensionMap.Add(".spl", "application/futuresplash");
ExtensionMap.Add(".sct", "text/scriptlet");
ExtensionMap.Add(".scd", "application/x-msschedule");
ExtensionMap.Add(".sst", "application/vndms-pkicertstore");
ExtensionMap.Add(".src", "application/x-wais-source");
ExtensionMap.Add(".sv4cpio", "application/x-sv4cpio");
ExtensionMap.Add(".tex", "application/x-tex");
ExtensionMap.Add(".tgz", "application/x-compressed");
ExtensionMap.Add(".t", "application/x-troff");
ExtensionMap.Add(".tar", "application/x-tar");
ExtensionMap.Add(".tr", "application/x-troff");
ExtensionMap.Add(".tif", "image/tiff");
ExtensionMap.Add(".txt", "text/plain");
ExtensionMap.Add(".texinfo", "application/x-texinfo");
ExtensionMap.Add(".trm", "application/x-msterminal");
ExtensionMap.Add(".tiff", "image/tiff");
ExtensionMap.Add(".tcl", "application/x-tcl");
ExtensionMap.Add(".texi", "application/x-texinfo");
ExtensionMap.Add(".tsv", "text/tab-separated-values");
ExtensionMap.Add(".ustar", "application/x-ustar");
ExtensionMap.Add(".uls", "text/iuls");
ExtensionMap.Add(".vcf", "text/x-vcard");
ExtensionMap.Add(".wps", "application/vnd.ms-works");
ExtensionMap.Add(".wav", "audio/wav");
ExtensionMap.Add(".wrz", "x-world/x-vrml");
ExtensionMap.Add(".wri", "application/x-mswrite");
ExtensionMap.Add(".wks", "application/vnd.ms-works");
ExtensionMap.Add(".wmf", "application/x-msmetafile");
ExtensionMap.Add(".wcm", "application/vnd.ms-works");
ExtensionMap.Add(".wrl", "x-world/x-vrml");
ExtensionMap.Add(".wdb", "application/vnd.ms-works");
ExtensionMap.Add(".wsdl", "text/xml");
ExtensionMap.Add(".xml", "text/xml");
ExtensionMap.Add(".xlm", "application/vnd.ms-excel");
ExtensionMap.Add(".xaf", "x-world/x-vrml");
ExtensionMap.Add(".xla", "application/vnd.ms-excel");
ExtensionMap.Add(".xls", "application/vnd.ms-excel");
ExtensionMap.Add(".xof", "x-world/x-vrml");
ExtensionMap.Add(".xlt", "application/vnd.ms-excel");
ExtensionMap.Add(".xlc", "application/vnd.ms-excel");
ExtensionMap.Add(".xsl", "text/xml");
ExtensionMap.Add(".xbm", "image/x-xbitmap");
ExtensionMap.Add(".xlw", "application/vnd.ms-excel");
ExtensionMap.Add(".xpm", "image/x-xpixmap");
ExtensionMap.Add(".xwd", "image/x-xwindowdump");
ExtensionMap.Add(".xsd", "text/xml");
ExtensionMap.Add(".z", "application/x-compress");
ExtensionMap.Add(".zip", "application/x-zip-compressed");
ExtensionMap.Add(".*", "application/octet-stream");
}
public static string GetMimeMapping(string fileExtension)
{
if (ExtensionMap.ContainsKey(fileExtension))
return ExtensionMap[fileExtension];
else
return ExtensionMap[".*"];
}
}
链接地址: http://www.djcxy.com/p/1272.html
下一篇: .NET中的文件扩展名和MIME类型