Tuesday, August 31, 2010

IFRAME based file download – Silverlight and beyond

Problem statement: You have to initiate file download in the browser with target = _self and in case of error on the server side you should not navigate away from the current page.

Why there is a problem: You initiate navigation to a download page. In case of successfully obtaining file from a server – you stay on the original page and FF/IE download window is shown. In case when there is an error on server side – result of your navigation with target _self will be an error page shown.

Though code provided here has some specific to Silverlight pieces, the idea is generic enough.

First, lets prepare a client side on the “initiator” page:

    <script type="text/javascript">
        $(document).ready(function () {
            $('#form1').append('<iframe id="downloadFrame" style="visibility: hidden; height: 0px; width: 0px; border: 0px"/>');
        function InitiateDownload(url) {
            $('#downloadFrame').attr('src', url);

Lets introduce a tiny helper class to wrap a call to InitiateDownload for Silverlight:

    public static class JavascriptHelper
        public const string DownloadFunctionName = "InitiateDownload";
        public static void StartDownload(Uri targetUri)
            HtmlPage.Window.Invoke(DownloadFunctionName, new object[] {targetUri.ToString()});

Now, we can call InitiateDownload from Silverlight (via a command here which is not required and don’t forget about cross domain hurdles if it is your case):

DownloadCommand = new DelegateCommand<string>(g => 

That is enough to get to the download dialog:


But lets add an ability to show a message box in case of a server-side error:


Following shows an example of amended download page (providing a placeholder for error message):

<head runat="server">
    <script src="scripts/jquery-1.4.2.min.js" type="text/javascript"></script>
    <form id="form1" runat="server">
        <p runat="server" id="ErrorHolder"></p>
    <script type="text/javascript">
        var errorText = $('p#ErrorHolder').text();

With code behind concept:

protected void Page_Load(object sender, EventArgs e)
                // Do your regular download stuff
            catch(Exception ex)
                // TODO: Log and only show user friendly sanitized message
                ErrorHolder.InnerText = ex.Message;
This is it and happy coding to You!

No comments: