0

When it comes to downloading files from server to client in blazor, there does not seem to be a best practice yet (please correct me if I am wrong). One good solution seems to be to implement a controller that returns a filestream (like it is done here: How to download in-memory file from Blazor server-side), there are also more client-side oriented solutions that involve javascript (like these: How can one generate and save a file client side using Blazor?). None of these however seem to exactly fit my problem.

What I want is a solution that lets me start parallel download streams of big files from the server to the client. Currently I am using a controller that fetches and zips files from a given directory in memory. I want the user to be able to start multiple download streams from the same page client-side. This does not work with my controller currently, since it redirects the user and the user has to wait until the download is finished to start the next. What is a good way to offer parallel downloads in blazor?

Here is what I currently have (simplified):

Controller:

[ApiController]
[Route("[controller]")]
public class DownloadController : Controller
{
    
    [HttpGet("{directory}/{zipFileName}")]
    [DisableRequestSizeLimit]
    public IActionResult DownloadZipFile(string directory, string zipFileName)
    {
        directory = System.Net.WebUtility.UrlDecode(directory);
        if (Directory.Exists(directory))
        {
            using (ZipFile zip = new ZipFile())
            {
                var files = Directory.GetFiles(directory, "*", SearchOption.AllDirectories);
                foreach (var f in files)
                {
                    zip.AddFile(f,Path.GetDirectoryName(f).Replace(directory, string.Empty));
                }
                using (MemoryStream memoryStream = new MemoryStream())
                {
                    zip.Save(memoryStream);
                    return File(memoryStream.ToArray(), "application/zip", String.Format(zipFileName));
                }
            }
        }
        else
        {
            // error handling
        }
    }
}

Razor Page:

<button @onclick="()=>DownloadZip(zipFilePath)">download file</button>

@code {
    protected string zipFilePath= @"C:\path\to\files";

    protected void DownloadZip(string zipFilePath)
    {
        NavigationManager.NavigateTo("api/download/" + System.Net.WebUtility.UrlEncode(zipFilePath) + "/ZipFileName.zip", true);
    }

}
dan-kli
  • 568
  • 2
  • 13

1 Answers1

1

Don't use a button and NavigationManager - instead use an anchor tag with the download attribute:

<a href=@GetZipURL(zipFilePath) target="_new" download>download file</a>
@code {
    protected string zipFilePath= @"C:\path\to\files";

    protected string GetZipURL(string zipFilePath)
    {
        return $"api/download/{System.Net.WebUtility.UrlEncode(zipFilePath)}/ZipFileName.zip";
    }
}

Your users can spam that download "button" as much as they like - the browser will handle parallel downloads.

If you want it to look like a button, that's just a bit of CSS styling.

Notice: The method GetZipURL is just returning a string - it is not redirecting or navigating.

I used target="_new" to prevent Blazor intercepting the event - but this is not required from .NET5 onwards.

Mister Magoo
  • 7,452
  • 1
  • 20
  • 35
  • Thanks for your help, I will try that next week. Isn't "target = _new" not supported in HTML 5 anymore? Also I want the user to stay on the same page, "_new" seems to behave similar to "_blank", but I don't want to redirect the user to a new page for the download ... I am not at work right now, but if I remember correctly I tried different attributes for the tag, and none of them that stayed on the same page worked for me. I will try it again on Monday. – dan-kli Jul 17 '21 at 09:51
  • 1
    Using "target='_new'" is just a thing to prevent old versions of Blazor from intercepting the event. You don't need it from .NET5 onwards. If you are using 3.1, then yes having it there is not a "supported" value, but target can contain any value you like as it is ignored when combined with the "download" attribute. – Mister Magoo Jul 18 '21 at 01:02
  • Thanks for the reply, im using .NET 5. I will try it out next week, and see if I get it to work. – dan-kli Jul 18 '21 at 18:25