Saturday, May 02, 2009
Downloading Files from a Silverlight Application
It seems that recently, I have been doing a lot of Silverlight development that includes uploading and downloading files of some sorts. File uploading of course is always a touchy subject in web applications (and I may blog about how we do that separately), but it may come as a bit of a surprise that even file downloading has it's problems in Silverlight.
Normally, when you offer a file download on a web site, you simply upload the file to its location, and then provide a link to it. When the user clicks that link, the browser starts a navigation away from the current page and to the linked URL, but then it realizes that the new address is a file download and not an HTML page, and it thus stops navigation (the current page remains visible) and a file-save dialog is displayed, usually allowing the user to either save or open the file.
In Silverlight, things are fundamentally similar. The main difference is that in Silverlight applications, one usually doesn’t navigate around in the browser as much. Instead, Silverlight code runs on the client, and instead of clicking on a link, Silverlight may offer up a button-click that initiates the download. The trick here is to programmatically initiate browser navigation from within that click event.
The following is a hypothetical click handler that initiates a file download:
Note: Very often you may want to download from a location relative to where the current Silverlight application is launched from. For instance, if the app is on www.MarkusEgger.com, you may want to download from www.MarkusEgger.com/Downloads/File.zip. You can relatively easily find out a relative path to that file like so:
var uri = new Uri(Application.Current.Host.Source, "../Downloads/File.zip”);
Note: The root of the app is usually one folder up, since the Silverlight control is deployed to the /ClientBin folder.
Now, while this fundamentally works fine, there is a problem here. If the browser that runs the Silverlight application is Internet Explorer, one has the additional issue that Internet Explorer may intercept the download and show a special security message. The user can then dismiss this message, but that can cause IE to re-load the current page. Theoretically, the download can then be re-initiated. This works kind of OK, in HTML pages where the same page is simply loaded again. It can be a problem with AJAX-heavy pages that may have drastically changed the page state since navigation. And it is a real problem with Silverlight, since that means that the page hosting the Silverlight control is re-loaded and put into its initial state.
This is unacceptable for Silverlight apps, because it is the equivalent of re-launching a windows app, and it may put the user at a completely different place from where they were before. Worse, it may even cause loss of application state and associated data. A nightmare!
The only solution I can offer to this problem is to open a new window programmatically when IE is the host, and perform the navigation within that window. IE will notice that the URL of the new window is a file and it will then close the window and instead show the save-file dialog. It all looks slightly odd, but it isn’t too bad, and it avoids the data loss issue. Here is a code example:
if (HtmlPage.BrowserInformation.Name == "Microsoft Internet Explorer")
This still isn't the entire story though. In many instances, IE will still block this, so what we resorted to is first navigating to another ASPX page instead of the actual file URL, and then redirecting to the real file URL.
There you go. Not the most elegant thing I have ever done, but it solves the fundamental problem…
Posted @ 10:03 PM by Egger, Markus (email@example.com)