How To: Save a file from a webservice without stopping the page from rendering

1k views Asked by At

I have a controller with two pages, Index and Download, when clicking download it retrieves a byte[] from the service and I use Response.BinaryWrite to save the file. The problem with this is that when using Response.Clear it stops the Download page from rendering but downloads the file successfully. Is there any way to download the file and render the page?

I'm using .NET 4, ASP, Monorail Castle, C#

I am aware that by using MVC I am able to use ActionResult and FileResult as return types for my views, I am however limited to using Monorail Castle due to it being an existing project I've taken ownership of recently and have no weight in changing it's technology just yet.

Below is my example code

public class MyController : Controller
{
    public void Index()
    {
        PropertyBag["includeZeroBalances"] = false;
        PropertyBag["toDate"] = DateTime.Today.ToShortDateString();
    }

    public void Download(bool includeZeroBalances, DateTime toDate)
    {
        MyProxy proxy = GetProxy();
        byte[] file = proxy.GetFile(includeZeroBalance, toDate);
        Response.Clear();
        Response.ContentType = "application/zip";
        Response.AppendHeader("Content-Disposition", "attachment; filename="TestFileName.zip");
        Response.BinaryWrite(file);
    }
}

Here is the Index page

${Form.FormTag({@action: 'Download'})}
    <table>
        <tr>
            <td>${Form.LabelFor("toDate", "To Date (yyyy/mm/dd):")}</td>
            <td><input type="text" id="toDate" name="toDate" value="${?toDate}" /></td>
        </tr>
        <tr>
            <td>${Form.LabelFor("includeZeroBalances", "Include Zero Balances:")}</td>
            <td>${Form.CheckboxField("includeZeroBalances")}</td>
        </tr>
        <tr>
            <td>&nbsp;</td>
            <td>${Form.Submit("Download", {@class: 'submitb'})}</td>
        </tr>
    </table>
${Form.EndFormTag()}

Here is the download page

<table>
    <tr>
        <td>Your file has been downloaded successfully</td>
    </tr>
</table>
2

There are 2 answers

0
Dwayne Hinterlang On BEST ANSWER

The answer I came up with is not as generic as I would have liked, yet this solution is functional.

public class MyController : Controller
{
    public void Index()
    {
        PropertyBag["includeZeroBalances"] = false;
        PropertyBag["toDate"] = DateTime.Today.ToShortDateString();
    }

    public void Download(bool includeZeroBalances, DateTime toDate)
    {
        MyProxy proxy = GetProxy();
        byte[] file = proxy.GetFile(includeZeroBalance, toDate);
        PropertyBag["downloadurl"] = "data:application/zip;base64," + System.Convert.ToBase64String(file);
    }
}

Here is the Index page

${Form.FormTag({@action: 'Download'})}
    <table>
        <tr>
            <td>${Form.LabelFor("toDate", "To Date (yyyy/mm/dd):")}</td>
            <td><input type="text" id="toDate" name="toDate" value="${?toDate}" /></td>
        </tr>
        <tr>
            <td>${Form.LabelFor("includeZeroBalances", "Include Zero Balances:")}</td>
            <td>${Form.CheckboxField("includeZeroBalances")}</td>
        </tr>
        <tr>
            <td>&nbsp;</td>
            <td>${Form.Submit("Download", {@class: 'submitb'})}</td>
        </tr>
    </table>
${Form.EndFormTag()}

Here is the download page

<table>
    <tr>
        <td>Your file has been generated successfully. <a href="${downloadurl}">Click here</a> to download your file</td>
    </tr>
</table>

You could also add javascript to automate downloading the file in a new window.

document.Ready(function()
{
    window.open("${downloadurl}");
});

Advantages of using this methodology to save files: No files are saved on the server. Slightly lower overhead when downloading files over approximately 300kb You don't overwrite the response data, hence the view will be rendered as per normal.

Disadvantages of using this methodology to save files: Not generic enough to be used on any browser of any version. If used for Static Files, when the file changes, the uri will have to be generated again since the file contents are embedded in the uri

I hope this helps others who seem to have been stuck on the same issue. I've seen this type of question across a number of boards with no straight forward solutions.

2
Mauricio Scheffer On

The 'right' way to do this would be using multipart responses, unfortunately some browsers don't support them.

What you can do is use a meta refresh like sourceforge and other sites do. For alternatives, see these questions: