Embedding ASP.NET Resources (ASP.NET 2.0 and Our Way Of Doing Things)
I've seen lots of examples for embedding resources in ASP.NET assemblies (which you can do in v2.0 without much trouble). However, they all seem to get it wrong (they send the bits, but forget the ContentType, which is a very important part of sending back requests). Microsoft's APS.NET 2 implementation gets it right, but requires a bunch of WebResourceAttributes to be appended to an assembly, which basically have a resource name and then a content type... this seems kind of strange to me, as IIS should already have the ContentType for the data you are adding.
It is even more strange to me that they chose to do it this way, since the framework already has a class called MimeMapping which allows for a lookup of a MimeType given a filename (of course, this is internal for some reason, is only used by the StaticFileHandler class, and ignores any IIS mappings you might have... but that is a debate for another day).
As this is one of the things our upcoming CMS product's assemblies do to store various web resources, I figured I'd chime in on another way to do this that provides a lot more flexibility at runtime. Of course, it requires some Win32 API calls, but it does the trick without the extra attribute lookups or a hard coded mapping of content types.
public class MimeLookup
{
public MimeLookup()
{
}
[DllImport("urlmon.dll", CharSet=CharSet.Auto)]
internal static extern int FindMimeFromData (
IntPtr pBC,
[MarshalAs(UnmanagedType.LPWStr)] string pwzUrl,
IntPtr pBuffer,
int cbSize,
[MarshalAs(UnmanagedType.LPWStr)] string pwzMimeProposed,
int dwMimeFlags,
[MarshalAs(UnmanagedType.LPWStr)] out string ppwzMimeOut,
int dwReserved );
static public string GetMimeType(string filename)
{
string mimeType = null;
FindMimeFromData(IntPtr.Zero, filename, IntPtr.Zero, 0, null, 0, out mimeType, 0);
return mimeType;
}
}
public class ResourceRequestHandler : IHttpHandler
{
public ResourceRequestHandler()
{
}
#region IHttpHandler Members
public void ProcessRequest(HttpContext context)
{
string resourceName = context.Request["ResourceName"];
if(resourceName == null)
{
throw new HttpException(404, "No resource was specified");
}
string typeName = context.Request["TypeName"];
if(typeName == null)
{
throw new HttpException(404, "No type was specified");
}
Type t = Type.GetType(HttpUtility.UrlDecode(typeName));
if(t == null)
{
throw new HttpException(404, "Invalid type was specified");
}
System.Reflection.Assembly a = System.Reflection.Assembly.GetAssembly(t);
using(Stream s = a.GetManifestResourceStream(HttpUtility.UrlDecode(resourceName)))
{
byte[] buffer = new byte[1024];
int read = 0;
while( (read = s.Read(buffer, 0, buffer.Length)) != 0 )
{
System.Reflection.ManifestResourceInfo ri = a.GetManifestResourceInfo(resourceName);
string filename = resourceName;
if(ri.FileName != null)
{
context.Response.AddHeader("content-disposition", "inline; filename="+filename);
}
context.Response.ContentType = MimeTypes.MimeLookup.GetMimeType(filename);
context.Response.OutputStream.Write(buffer, 0, read);
}
}
}
public bool IsReusable
{
get
{
return true;
}
}
public static string GetUrl(Type t, string resourceName)
{
return "GetResource.ashx?TypeName="+HttpUtility.UrlEncode(t.AssemblyQualifiedName)+"&ResourceName="+HttpUtility.UrlEncode(resourceName);
}
#endregion
}