6

I have a server side blazor app which builds up a lot of data and when a user clicks a button will use that data to generate an excel file. All of that is working fine. But my question is what is the appropriate way to download that in-memory file? I know I can save it to the web server disk and do a redirect or something like that to download it I would prefer not to have to save the file to disk if I don't have to.

devlife
  • 15,275
  • 27
  • 77
  • 131

2 Answers2

7

The solution I ended up using was JS Interop to redirect to the file which then downloaded it.

public async Task DownloadFileAsync(string path)
{
    await Js.InvokeAsync<string>("downloadFile", path);
}

// In JS
function downloadFile(filename) {
    location.href = '/api/downloads/' + filename;
}
devlife
  • 15,275
  • 27
  • 77
  • 131
  • 1
    At this time, you don't need JS to redirect: https://stackoverflow.com/questions/55340677/how-to-redirect-to-blazor-with-token – dani herrera Nov 25 '19 at 07:48
0

You can do something like this

[Route("api/[controller]"]
[ApiController]
public class DownloadController : ControllerBase
{
    [HttpGet, DisableRequestSizeLimit]
    public async Task<IActionResult> Download()
    {
        var memory = new MemoryStream();
        await using(var stream = new FileStream(@"pathToLocalFile", FileMode.Open))
        {
            await stream.CopyToAsync(memory);
        }
        memory.Position = 0;
        //set correct content type here
        return File(memory, "application/octet-stream", "fileNameToBeUsedForSave");
    }
}

And on a razor side

<button @onclick="onClick">download</button>

@code {

   private async Task onClick(MouseEventArgs e)
   {
        NavigationManager.NavigateTo("api/download", true);
   }
}
Martin Ch
  • 1,337
  • 4
  • 21
  • 42
  • 1
    It may be an improvement to skip using the MemoryStream - there's not much point using a FileStream (which is a Stream) to read a file into memory then seeking the MemoryStream back to 0 and sending it into a method that needs a Stream (which a FileStream is) - just declare the File using the FileStream for the Stream argument. If it's a 500Mb file you'll burn 500Mb of memory to load it into a MemoryStream and it costs a truckload more resource in doubling a 256 byte array N times to read 512Mb, and copying all the bytes over each time there is a doubling – Caius Jard May 01 '21 at 06:09
  • The Question is about Blazor Server side not Assembly – MarchalPT Jul 16 '21 at 16:27
  • Although this proposed solution tries to address streaming a file, it doesn't account for the question asserting that the file is already in memory (generated). I think @CaiusJard whilst accurate about the usage of memory, got hung up on the details, and missed the simpler point. I believe the proposed solution just misses suggesting you save the file to disk in order to access it from a HTTP request, or else you have to find a way to store the generated file in memory in such a way you can identify it, and access it from a HTTP request. It should also become stale and dispose after time. – Simon Miller Jul 23 '21 at 12:17