As part of the Silverlight application I’m working on, I want to reduce our bandwidth requirements as well as give the users the option of having a faster experience by offering to cache the images locally on the user’s Isolated Storage. However, with Silverlight you only get 1 MB of Isolated Storage when online in-the-browser, and with Silverlight 3 you get 25 MB out-of-browser, but I need more than that. The problem is I didn’t want to download each file first to figure out how much space I needed to request from the Isolated Storage mechanism (because you have to specifically ask to increase the quota to a certain size, and then the user must accept it in a dialog).
So HTTP headers come to the rescue. Using the HEAD command, I can get something that looks like the following from each of the images I know I need to cache, without having to download the whole thing:
Notice the Content-Length: 26529. That’s the size, in bytes, of that file. And also notice that there’s no data returned after the headers. That means that this request will go VERY fast, and I can blow through them all very quickly. Note that this is also useful for finding the last modified date, which I will be using to determine if the locally cached file is out of date.
cURL gives a good interface for calling the HTTP HEAD verb, all you have to do is add the “--head” argument. But how do we do this in .NET?
Answer: use System.Net.HttpWebRequest!
Notice line 8: here we’re telling the request object to use the HEAD verb so that no data is returned. Then, we call request.GetResponse to get the response, and the ContentLength is a property on that returned WebResponse object. Pretty easy! The output is:
And we see that the values match, of course. Make sure you put that request.Method = “HEAD” line in there. Other examples on the web left out that line, and so it was actually doing a full request.
If you notice above, I didn’t give a type for the request and response objects. Even though I called HttpWebRequest.Create(), it returns a WebRequest object. Although that works for getting the content length, there’s a bunch more you can do with the HttpWebRequest and HttpWebResponse objects. Let’s try the caching concept I mentioned earlier.
Using the HTTP Header of If-Modified-Since, we can give the web server the date of our cached version, and if it hasn’t changed since then, it returns an HTTP 304 Not Modified status code. If it has changed, it returns HTTP 200 OK. The HttpWebRequest actually throws a WebException if it gets a 304 code back, so we have to catch it.
First, in the above code, we’re setting the HttpWebRequest.IfModifiedSince property to 1/1/2008. (The file was modified in 2006.) Then, if it’s been modified since 1/1/2008, we print out the response status code and description, as well as the content length and last-modified date. If it returns 304, it throws a WebException, but in the ex.Response property we can access the status code and description and other headers. So here’s what this example returns:
So now we know that we do not need to get the data, because it hasn’t been modified since our request date. But let’s move that request.IfModifiedSince date back to 2005 and see what happens.
So you can see that the file was modified in 2006, after our request date of 2005, so we should get the data.
Note that if you’re strictly doing a cache-update scenario and you don’t need to pay attention to the file size, you don’t have to just do a HEAD request. You could do a full GET and have the data returned if it has been modified, and if it hasn’t, it’ll throw a 304 Not Modified without any data.
Now, I’m not sure if the whole HttpWebRequest and HttpWebResponse API is available in Silverlight yet, but this at least gets me started.
Hope this helps!
