'.net MVC How do I download a file and stay on the same page?

I am developing a page where a user can download a .zip containing a number of .pdf files. The functionality handling the download and the compressing works fine, my issue is that when I press the download button, it puts my page into an endless loop. It is as if it is loading the next View, but since the ActionResult returns a File, not a View or a redirect, it will never get to load a page. I have tried to set the formtarget to _blank, so that the download opens in a new tab, but that still puts the original tab in an endless loop.

Here is my controller method:

[HttpPost]
        public ActionResult DownloadCertificatesByModel([Bind(Include = "OrderNumber,ChosenLanguages,ChosenTemplates")] OrderViewModel viewModel)
        {
            var certificates = GetCertificatesByOrderNumber(viewModel.OrderNumber, viewModel.ChosenLanguages, viewModel.ChosenTemplates);

            using (var memoryStream = new MemoryStream())
            {
                using (var zipArchive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
                {
                    foreach(var cert in certificates)
                    {
                        //If the file actually exists
                        if (cert.Value)
                        {
                            zipArchive.CreateEntry(cert.Key.CertificateName);
                        }
                    }
                }

                return File(memoryStream.ToArray(), "application/zip", viewModel.OrderNumber + "_certificates.zip");
            }
        }

Here is the part of my Razor View in question:

<input type="submit" value="Download All Certificates" class="btn btn-default" formaction="DownloadCertificatesByModel" style="margin-right:10px" formtarget="_blank" />

How can I have a controller action that returns a file but keeps the current page as it is, meaning no redirecting or anything, just initiating a download?

UPDATE: I should mention that the two "Chosen" variables from the model are List<SelectListItem> which means that when I try to follow @cacalonga's suggestion (change POST to GET), I get an error that the query string exceeds the max allowed length. To solve this I have tried to increase the MaxQueryLength in the Web.config, but this does not work either as the query is extremely long as it contains all the SelectListItems.



Solution 1:[1]

You should use httpget method.

[HttpGet]
    public ActionResult DownloadCertificatesByModel([Bind(Include = "OrderNumber,ChosenLanguages,ChosenTemplates")] OrderViewModel viewModel)
    {
        var certificates = GetCertificatesByOrderNumber(viewModel.OrderNumber, viewModel.ChosenLanguages, viewModel.ChosenTemplates);

        using (var memoryStream = new MemoryStream())
        {
            using (var zipArchive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
            {
                foreach(var cert in certificates)
                {
                    //If the file actually exists
                    if (cert.Value)
                    {
                        zipArchive.CreateEntry(cert.Key.CertificateName);
                    }
                }
            }

            return File(memoryStream.ToArray(), "application/zip", viewModel.OrderNumber + "_certificates.zip");
        }
    }

On cshtml side:

<form action="/YourActionController/DownloadCertificatesByModel" method="get">
<input type="text" name="OrderNumber" value="" />
<input type="text" name="ChosenLanguages" value="" />
<input type="text" name="ChosenTemplates" value="" />
<input type="submit" value="Download All Certificates" class="btn btn-default" />

/form>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1