DiskCache plugin strategy for e-mail images with tracking - c#

Currently we're sending thousands of e-mails every day to our customers. An e-mail contains an image url with a specific Message ID. When the user downloads that image, I mark that message as opened in the database. For example:
http://cdn.mydomain.com/companylogos/{guid}.png?h=100&w=100&messageid=123
At this moment, every image request requires me to get the byte[ ] of the image from cache. Resize it. And Return it. This all takes place in a httphandler.
I would like to take the advantage of ImageResizer.net and the Disk Cache plugin. However, I still need to get the MessageId-querystring parameter. So I'm thinking about this solution.
Extending HttpModule
public class CustomInterceptModule : InterceptModule
{
protected override void HandleRequest(HttpContext context, HttpModuleRequestAssistant ra, IVirtualFile vf)
{
var messageIdParam = context.Request.QueryString["messageId"];
int messageId;
if (!string.IsNullOrWhiteSpace(messageIdParam) && int.TryParse(messageIdParam, out messageId))
{
// Do something with the ID here
}
base.HandleRequest(context, ra, vf);
}
}
Does this method still creates high performance, disk cached, results? Or am I interrupting this because I'm extending the HttpModule and I'm adding my own logic in there.

If your goal is to increase performance and/or reduce the work you server needs to do, I would suggest that you should not use a full-size image for tracking purposes, but instead use an invisible 1x1.png image to track whether or not a message was opened, and simply use a standard img url that everybody shares (that can be distributed on a CDN and/or cached) for the image that people actually see.

You can simply add an event handler to ImageResizer.Config.Current.Pipeline.AuthorizeImage - this will be called for all image requests, and is less brittle than subclassing the HttpModule.

Related

Proper usage for MvxFileDownloadCache.Clear

I'm trying to cleanup unwanted http image data that I have loaded via MvxImageViewLoader.
I've found the function clear in the FileDownloadCache which seems to do what I need.
var downloadCache = Mvx.Resolve<IMvxFileDownloadCache>();
downloadCache.Clear(_imageChart1ViewLoader.ImageUrl);
Periodically it calls a function (once a second by the looks of it) which deletes the files in the private list
private readonly List<string> _toDeleteFiles = new List<string>();
Clear adds the image by url to that list.
Except once I call this function I'm still able to see the image. I.e. it stays in memory.
So really I need to know where is a good place to be calling Clear and am I using it in the correct way. Currently I call it every time I exit my DetailView which downloads an image from a URL.
MvvmCross v4.2.2 (latest)

Checking that async data load compete before accessing data

I'm using MVVMlight SimpleIoC container, particularly to store quite a large volume of data, which is accessible throughout the application. When the application is started, I launch an asynchronous process, which prepares the data.
private MethodOfOriginalThread()
{
(new Thread(this.LoadRegistry)).Start();
}
private void LoadRegistry()
{
int meetingId = SimpleIoc.Default.GetInstance<MeetingDetails>().MeetingId;
List<Shareholder> registry = this.dataService.SearchRegistry(meetingId, string.Empty);
SimpleIoc.Default.Register(() => registry);
}
The process of obtaining the data takes approximately 10 seconds. After the process is complete, I save the data to container, as seen in the code above.
Later from a different viewModel I'm accessing this data:
this.shareholders =
new NotifyObservableCollection<Shareholder>(
SimpleIoc.Default.GetInstance<List<Shareholder>>()
);
The problem arises when this access is made before the actual load completes. Since the viemodels are very loosely coupled, I cannot refer to one from another. Sending messages may result in hyge amount of code, since these results are widely used in the application, and each viewmodel should have the message handling code. How can I check and wait, whether the data has been successfully loaded? An endless loop, which waits for required object in the container, sounds halfway ok, since the operation should eventually be completed, but I'm worried that this is not the most elegant solution. And also I need to let the user have some feedback that the data is still being loaded, so he waits patiently, and does not press too many buttons. This is quite challenging with an endless loop and frozen application.

OutOfMemoryException # WriteableBitmap # background agent

I have a Windows Phone 8 App, which uses Background agent to:
Get data from internet;
Generate image based on a User Control that uses the data from step 1 as data source;
In the User Control I have Grid & StackPanel & some Text and Image controls;
When some of the Images use local resources from the installation folder (/Assets/images/...)
One of them which I used as a background is selected by user from the phone's photo library, so I have to set the source using C# code behind.
However, when it runs under the background, it get the OutOfMemoryException, some troubleshooting so far:
When I run the process in the "front", everything works fine;
If I comment out the update progress, and create the image directly, it also works fine;
If I don't set the background image, it also works fine;
The OutOfMemoryException was thrown out during var bmp = new WriteableBitmap(480, 800);
I already shrink the image size from 1280*768 to 800*480, I think it is the bottom line for a full screen background image, isn't it?
After some research, I found out this problem occurs because it exceeded the 11 MB limitation for a Periodic Task.
I tried use the DeviceStatus.ApplicationCurrentMemoryUsage to track the memory usageļ¼š
-- the limitation is 11,534,336 (bit)
-- when background agent started, even without any task in it, the memory usage turns to be 4,648,960
-- When get update from internet, it grew up to 5,079,040
-- when finished, it dropped back to 4,648,960
-- When the invoke started (to generate image from the User Control), it grew up to 8,499,200
Well, I guess that's the problem, there is little memory available for it to render the image via WriteableBitmap.
Any idea how to work out this problem?
Is there a better method to generate an image from a User Control / or anything else?
Actually the original image might only be 100 kb or around, however, when rendering by WriteableBitmap, the file size (as well as the required memory size I guess) might grew up to 1-2MB.
Or can I release the memory from anywhere?
==============================================================
BTW, when this Code Project article says I can use only 11MB memory in a Periodic Task;
However, this MSDN article says that I can use up to 20 MB or 25MB with Windows Phone 8 Update 3;
Which is correct? And why am I in the first situation?
==============================================================
Edit:
Speak of the debugger, it also stated in the MSDN article:
When running under the debugger, memory and timeout restrictions are suspended.
But why would I still hit the limitation?
==============================================================
Edit:
Well, I found something seems to be helpful, I will check on them for now, suggestions are still welcome.
http://writeablebitmapex.codeplex.com/
http://suchan.cz/2012/07/pro-live-tiles-for-windows-phone/
http://notebookheavy.com/2011/12/06/microsoft-style-dynamic-tiles-for-windows-phone-mango/
==============================================================
The code to generate the image:
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
var customBG = new ImageUserControl();
customBG.Measure(new Size(480, 800));
var bmp = new WriteableBitmap(480, 800); //Thrown the **OutOfMemoryException**
bmp.Render(customBG, null);
bmp.Invalidate();
using (var isf = IsolatedStorageFile.GetUserStoreForApplication())
{
filename = "/Shared/NewBackGround.jpg";
using (var stream = isf.OpenFile(filename, System.IO.FileMode.OpenOrCreate))
{
bmp.SaveJpeg(stream, 480, 800, 0, 100);
}
}
}
The XAML code for the ImageUserControl:
<UserControl blabla... d:DesignHeight="800" d:DesignWidth="480">
<Grid x:Name="LayoutRoot">
<Image x:Name="nBackgroundSource" Stretch="UniformToFill"/>
//blabla...
</Grid>
</UserControl>
The C# code behind the ImageUserControl:
public ImageUserControl()
{
InitializeComponent();
LupdateUI();
}
public void LupdateUI()
{
DataInfo _dataInfo = new DataInfo();
LayoutRoot.DataContext = _dataInfo;
try
{
using (var isoStore = IsolatedStorageFile.GetUserStoreForApplication())
{
using (var isoFileStream = isoStore.OpenFile("/Shared/BackgroundImage.jpg", FileMode.Open, FileAccess.Read))
{
BitmapImage bi = new BitmapImage();
bi.SetSource(isoFileStream);
nBackgroundSource.Source = bi;
}
}
}
catch (Exception) { }
}
When DataInfo is another Class within the settings page that hold data get from the internet:
public class DataInfo
{
public string Wind1 { get { return GetValueOrDefault<string>("Wind1", "N/A"); } set { if (AddOrUpdateValue("Wind1", value)) { Save(); } } }
public string Wind2 { get { return GetValueOrDefault<string>("Wind2", "N/A"); } set { if (AddOrUpdateValue("Wind2", value)) { Save(); } } }
//blabla...
}
If I comment out the update progress, and create the image directly, it also works fine I think you should focus on that part. It seems to indicate some memory isn't freed after the update. Make sure all the references used during the update process go out of scope before rendering the picture. Forcing a garbage collection can help too:
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect(); // Frees the memory that was used by the finalizers
Another thing to consider is that the debugger is also using a lot of memory. Do a real test by compiling your project in "Release" mode and deploying on a phone to make sure you're running out of memory.
Still, I have already been in that situation so I know it may not be enough. The point is: some libraries in the .NET Framework are loaded lazily. For instance, if your update process involves downloading some data, then the background agent will load the network libraries. Those libraries can't be unloaded and will waste some of your agent's memory. That's why, even by freeing all the memory you used during the update process, you won't reach back the same amount of free memory you had when starting the background agent. Seeing this, what I did in one of my app was to span the workload of the background agent across two executions. Basically, when the agents executes:
Check in the isolated storage if there's pending data to be processed. If not, just execute the update process and store all needed data in the isolated storage
If there is pending data (that is, in the next execution), generate the picture and clear the data
It means the picture will be generated only once every hour instead of once every 30 minutes, so use this workaround only if everything else fail.
The larger memory limit is for background audio agents, it clearly states that in the documentation. You're stuck with 11 MB, which can really be a big pain when you're trying to do something smart with pictures in the background.
480x800 adds a MB to your memory because it takes 4 bytes for every pixels, so in the end it's around 1.22MB. When compressed in JPEG, then yes - it makes sense that it's only around 100KB. But whenever you use WriteableBitmap, it gets loaded into memory.
One of the things you could try before forcing the GC.Collect as mentioned in another answer is to null things out even before they go out of scope - whether it's a BitmapImage or a WriteableBitmap. Other than that you can try removing the Image object from the Grid programmatically when you're done and setting the Source of it also to be null.
Are there any other WriteableBitmap, BitmapImage or Image objects you're not showing us?
Also, try without the debugger. I've read somewhere that it adds another 1-2MB which is a lot when you have only 11 MB. Although, if it crashes so quickly with the debugger, I wouldn't risk it even if it suddenly seams OK without the debugger. But just for testing purposes you can give it a shot.
Do you need to use the ImageUserControl? Can you try doing step by step creating Image and all the other objects, without the XAML, so you can measure memory in each and every step to see at which point it goes through the roof?

Using Response Flush in WebControl

I'd like to use some sort of stepwise rendering in a webcontrol, here is an example (not working as supposed)
public class Price : WebControl
{
protected override void Render(HtmlTextWriter output)
{
HttpResponseBase response = new HttpResponseWrapper(new HttpResponse(output));
//Panel basePanel = new Panel() { ID = "basePanel" };
//Controls.Add(basePanel);
//var loaderImage = LoaderAnimation();
//Controls.Add(loaderImage);
System.Threading.Thread.Sleep(500);
Controls.Add(new LiteralControl("aaa "));
response.Flush();
System.Threading.Thread.Sleep(500);
Controls.Add(new LiteralControl("bbb "));
response.Flush();
System.Threading.Thread.Sleep(500);
Controls.Add(new LiteralControl("ccc "));
base.RenderContents(output);
}
}
This should render at each flush. Maybe this is a terrible technique but Id love some advice in this.
Your approach probably won't work as expected. If you want step-wise rendering (I'm not asking why), you should resolve to something like AJAX.
There's too much involved (the network, buffering on client and server-side, rendering in the browser which is very dependent on end-tags of other elements than the current etc) that you can use an approach where you simply pause generation of content intermittently.
If you want to show something like the progress of a longer-taking process on the server, let another background thread do the job and let the client poll each X seconds for updates. JSON and AJAX are your friend there and you can show a slowly growing page/content to the user.
This has the added effect that you can render a whole page first (i.e. valid HTML including html end-tag) and then update parts in the middle of the HTML page.
To do it the way you're attempting, you'd need to render the controls with (possibly) a .ToString() call. Then, call response.Write(yourString) before calling response.Flush().
The approach Abel mentions is, IMO, a better one.
This blog post indicates that you'd need to call response.Flush twice... that may work with what you're doing. When I've needed to do similar things I've used Abel's approach.
http://blogs.microsoft.co.il/blogs/itai/archive/2009/02/08/improve-your-asp-net-website-user-experience-flush-down-your-partial-response.aspx

How would I load photos in the background in a wpf desktop app so that it doesn't take a few seconds to load the next photo in the gallery?

Currently my program reads images and text in a record from an xml file, displays them on the screen, and then the click of the previous/next buttons moves to the next record. However, it seems to need a few seconds loading time between each photo and I'd like it to be instant, like how Windows Photo Gallery would...or Facebook photos (bear in mind this is not a web app).
I searched found a few similar situations to mine but none seemed to fit my situation. I tried making a class, based on my search, to deal with background loading and calling it in my program, but it's fraught with error and probably won't even do what I want it do:
//ImageManager.cs
class ImageManager
{
private Dictionary<string, Image> images = new Dictionary<string, Image>();
public Image get(string s)
{ // blocking call, returns the image
return load(s);
}
private Image load(string s)
{ // internal, thread-safe helper
lock (images)
{
if (!images.ContainsKey(s))
{
Image img = images.Add(s, img); //load the image s - ERROR cannot implicitly convert type void to image. Void??
return img;
}
return images[s];
}
}
public void preload(params string[] imgs)
{ // non-blocking preloading call
foreach (string img in imgs)
{
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += (s, e) => { load(img); }; // discard the actual image return
bw.RunWorkerAsync();
}
}
}
//MainWindow.cs
ImageManager im = new ImageManager();
im.preload("Data/Images"); // Errors - im is a field but used like a type/token '('
Many thanks in advance
Your ImageManager should work with ImageSources, not Images. Even if you get your current code to work you'll find that your UI still hangs because you have no choice but to perform the work on the UI thread. If you instead deal with ImageSources, you can load them on a background thread and then freeze them in order to use them from the UI thread. This frees you to pre-emptively load images, or to show a loading animation whilst they load.
BitmapFrame.Create is likely the method you want to be using to load the images.
Consider caching scaled down images - 1:1 of what you want to show, or even smaller. This way loading of preview will be much faster and if user looks at the image long enough you can load full image.
With modern photos original size of the image is usually way bigger than can be normally diaplayed. So if you always read original images you spend large amount of disk IO on something that will never be shown.
Usual note: it may not be case in your program. As with any performance issues measure, than optimize.

Categories