For most applications when it comes to localization, most of the tasks at hand have to do with string resource localization. However, resources in .NET also support binary resource streams so you can store images and other data in resource files. Part of the reason for this is that Resx resources serve more purposes than localization – they are also meant as a way to store reusable larger size content outside of the actual source code of an application.
Behind the scenes .NET stores resources as files that are linked into Resx files as project relative paths and then sucked into the actual satellite resource assembly when the project is compiled. The invariant/default resource assembly is merged into the main assembly while any other localized versions are stored externally in the bin folder with the appropriate locale extension like Resources.de-DE.dll.
Inside of the actual .Resx file, when you open it with the plain XML editor in Visual Studio or a text editor from Windows you get:
<data name="Calendar.png" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Calendar.png.png;System.Drawing.Bitmap, System.Drawing,
Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
The strongly type property on the Resource then is generated returning a BMP image like this:
public static System.Drawing.Bitmap Calendar_png {
get {
object obj = ResourceManager.GetObject("Calendar.png", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
Now this works just fine, but I wonder what the value of this functionality really is – unless you’re building a Windows desktop application for WinForms or WPF. Bitmaps are huge memory hogs especially if you import them generically without applying appropriate encoding and they don’t retain the image type information very well. If you load a binary jpeg image into a bitmap it recognizes the jpeg image, but it loses a buch of the internal encoding and formatting information so that when you save it you have to explicitly specify the image type, encoders etc. all over again – all the original image information and settings are lost.
Are BitMaps as Resources Useful?
In short if you want to do something other than directly display the images as is as you might in Windows apps, it’s very rare that you will use bitmaps directly in code. Instead you’re much more likely to take the image and stream it somewhere – either to disk, or out to a network stream in which case Bitmaps are actually a royal pain in the butt.
What am I talking about? Behind the scenes Bitmap uses GDI+ and there are a number of issues with this. First GDI+ is not Web friendly – supposedly it doesn’t support multi-threaded operation and while I’ve not run into any major issues with this for small image conversion routines I have heard others doing more intense image processing on busy sites that have. Microsoft doesn’t recommend using GDI+ processing in Web apps – even though everybody does it anyway until it breaks.
The other major issue is image conversion. There are all sorts of weird quirks you have to be aware of when exporting images and these quirks are often specific to specific image formats. For example, if you want to export PNG files and save them to disk you can’t write the image back to the same file you loaded it from because the file is locked – it works fine in other image formats. JPEG formats have major issues saving out to disk often failing with mysterious ‘A generic error occurred in GDI+’ errors. In fact this post is in response to one of those errors that I simply could not resolve:
public static string BitmapToEmbeddedHtmlImage(Bitmap bitmap, ImageFormat format, string extraAttributes = null)
{
byte[] data;
using (var ms = new MemoryStream(1024))
{
if (format == ImageFormat.Jpeg)
{
EncoderParameter qualityParam = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality,(long) 85);
ImageCodecInfo jpegCodec = ImageUtils.Encoders["image/jpeg"];
EncoderParameters encoderParams = new EncoderParameters(1);
encoderParams.Param[0] = qualityParam;
bitmap.Save((MemoryStream) ms, jpegCodec, encoderParams);
}
else
bitmap.Save(ms, format);
data= ms.ToArray();
}
return BitmapToEmbeddedHtmlImage(data, format, extraAttributes);
}
The code works fine with binary image data for PNG, GIF, TIFF and BMP images, but with JPEG (first using the same generic code as the other formats and then using this specialized code that includes the specific encoder) it simply fails with A generic error occurred in GDI+. I’ve not been able to figure out why this is happening. And this isn’t the first time I’ve run into this issue.
Later:
As it turns out I found the culprit for errors which is that *some* jpeg images require that the stream that the BitMap is read in from has to be kept open beyond the initial creation of the bitmap. Specifically, if I do new BitMap(ms) the MemoryStream instance cannot be disposed explicitly after the Bitmap creation. Oddly thought this does not occur with all jpeg images – just with some of them, and not at all with other bitmap types. I ended up special casing jpegs where the memory stream is not immediately disposed but just assigned rather than using a using statement to wrap the MemoryStream. Here’s a link to an example that demonstrates the problem.
Bitmaps also rely on System.Drawing which is one of the API’s that’s going away in the new CoreCLR and ASP.NET vNext (if you’re not using the full CLR).
So I’m wondering if I’m doing anybody any favor by actually going through the trouble of supporting Bitmap types at all or whether it’s just easier and cleaner to simply return raw binary data (byte[]) instead.
Binary Resources in Westwind.Globalization
And this is a perfect segue of how this relates to Westwind.Globalization which is a database resource manager/provider library that includes a host of tools for working with database or resx resources.
The primary target for this library are Web applications, and here is where I’m struggling to see that the generic Bitmap format even makes any sense.
Over the last few days I implemented support for uploading binary resources into a resource via the Web admin interface, so you can easily upload images. Here’s what the new UI for this looks like:
The file resource upload button lets you pick a file from the local machine and it uploads it to the server as a resource. On the server the binary data is stored as a raw byte[] stream in the database and the actual resource items show the values as filename plus the type (the same way that Resx does sans the path).
Note that you can create separate files for each locale so you can have different images for different languages for example. Or you can use a single image on the Invariant locale and let Resource Fallback handle serving that image to all locales. This is all as you’d expect.
Additionally if you now generate strongly typed resources from the database resources in this application you also get the type specified above – in this case System.Drawing.Bitmap because the value is an image.
public static System.Drawing.Bitmap PingPongImage
{
get
{
return (System.Drawing.Bitmap) GeneratedResourceHelper.GetResourceObject("Resources",
"PingPongImage",ResourceManager,GeneratedResourceSettings.ResourceAccessMode);
}
}
This code looks slightly different than the Resx generated files as GetResourceObject can handle Resx resources, the DbResourceManager with any of its support Resource sources, or the ASP.NET ResourceProvider.
Bitmap – now what?
Strongly typed resources are nice as you can simply reference resources from classes and get Intellisense. There’s nothing like getting Intellisense, strong type compile time checking and refactoring of resource names so you can do stuff like this in an ASP.NET Razor page for example:
But what the heck do you do with a Bitmap image?
This comes back to the function I showed earlier that takes a bitmap image and converts it to directly embed into an HTML document as a base64 encoded string. But as I mentioned – I ran into major issues with certain image types – some JPeg images in particular.
The same function would be a heck of a lot cleaner and simpler if we could just feed the binary content directly:
/// <summary>
/// Renders an HTML IMG tag that contains a raw byte stream's image content
/// inline of the HTML document. Userful for resources.
/// </summary>
/// <param name="data"></param>
/// <param name="format"></param>
/// <param name="extraAttributes"></param>
/// <returns></returns>
public static string BitmapToEmbeddedHtmlImage(byte[] data, ImageFormat format, string extraAttributes = null)
{
string contentType = "image/jpeg";
if (format == ImageFormat.Png)
contentType = "image/png";
else if (format == ImageFormat.Gif)
contentType = "image/gif";
else if (format == ImageFormat.Bmp)
contentType = "image/bmp";
StringBuilder sb = new StringBuilder();
sb.Append("<img src=\"data:" + contentType + ";base64,");
sb.Append(Convert.ToBase64String(data));
sb.Append("\"");
if (!string.IsNullOrEmpty(extraAttributes))
sb.Append(" " + extraAttributes);
sb.Append(" />");
return sb.ToString();
}
Which has to do no special formatting of the data. It simply takes the raw data converts it and off it goes – in this case to convert to base64 and embed, or in the case of the JavaScript resource handler that can serve individual resources like images.
To Bitmap or Not To Bitmap
So I have this quandry that I’m trying to resolve. Should I keep close compatibility with .NET’s Resx implementation and store image resources as bitmaps or do what I think is the much more prudent thing and simply store the raw data and return the data in that same raw data.
The only downside to raw data that I see is that it would potentially break non-Web projects that use image resources. Since the new version of Westwind.Globalization supports importing arbitrary Resx resources and then exporting them again there’s a small chance that this would be a problem. For everything else – especially Web projects I suspect raw resources are the preferred way to go.
What do you think?
Other Posts you might also like