Skip to content

Blazor Server File Streaming freezes UI #33752

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
dotnetspark opened this issue Jun 22, 2021 · 7 comments
Closed

Blazor Server File Streaming freezes UI #33752

dotnetspark opened this issue Jun 22, 2021 · 7 comments
Labels
area-blazor Includes: Blazor, Razor Components feature-blazor-server ✔️ Resolution: Answered Resolved because the question asked by the original author has been answered. Status: Resolved

Comments

@dotnetspark
Copy link

dotnetspark commented Jun 22, 2021

Background

I have a Blazor Server hosting model with an API to download files. It could be fetched from a Azure blob storage or file share. In the Client I have a page with a button that calls the API to download a file. As of now, everything works, but the UX is bad since the UI is frozen until the current download is completed. If the file to download is 1GB, UI will be frozen for a long time. Is there a way to improve this?

Server - File API

[HttpPost]
public async Task<ActionResult> Download([FromBody] FileModel model)
{
    try
    {
        var result = FileService.GetFileStream(model);
        var provider = new FileExtensionContentTypeProvider();
        if (!provider.TryGetContentType(xfRequest.FileFullName, out var contentType))
        {
            contentType = "application/octet-stream";
        }

        return File(result, contentType, xfRequest.FileFullName);
    }
    catch (Exception e)
    {
        // do something
    }
}

Client - index.js

function download(filename, bytesBase64) {
    var link = document.createElement('a');
    link.download = filename;
    link.href = "data:application/octet-stream;base64," + bytesBase64;
    document.body.appendChild(link); // Needed for Firefox
    link.click();
    document.body.removeChild(link);
}

Client - Shared - FileExtensions.cs

public static class FileExtensions
{
    public static async Task Download(this IJSRuntime js, string filename, byte[] data)
    {
        await js.InvokeVoidAsync("download", filename, Convert.ToBase64String(data));
    }
}

Client - Pages - Files

<button class="btn btn-primary" @onclick="Download" text="Download" />

\\ more code here

@code
{
    /* code left out here*/

    private async Task DownloadFile()
    {
        using (var response = await this.Http.PostAsJsonAsync<FileModel>("api/file/download", model))
        {
            var content = await response.Content.ReadAsByteArrayAsync();
            await JS.Download(model.FileName, content);
        }
    }
}
@javiercn javiercn added area-blazor Includes: Blazor, Razor Components feature-blazor-server labels Jun 22, 2021
@javiercn
Copy link
Member

@ylr-research thanks for contacting us.

I believe the issue you are facing is likely that you are sending a gigantic blob of data from the server to the browser as a string (based on your Client - Shared - FileExtensions.cs code)

We would suggest you break that into multiple JS interop calls and collect the results at the end into a single payload. We are working on a feature that will make this much easier to do. See here for details.

@dotnetspark
Copy link
Author

Thanks @javiercn for the quick response.

@pranavkm pranavkm added the ✔️ Resolution: Answered Resolved because the question asked by the original author has been answered. label Jun 22, 2021
@ghost ghost added the Status: Resolved label Jun 22, 2021
@dotnetspark
Copy link
Author

dotnetspark commented Jun 22, 2021

@javiercn do you happen to know of any POC on how to do it? I'm assuming the API should yield return chunks and once completed I should do the single payload, but have never done that.

@javiercn
Copy link
Member

@ylr-research You can check our InputFile component and copy the implementation but do it the other way around, other than that we don't have anything built, that's what we are adding for 6.0

@dotnetspark
Copy link
Author

fair enough. Thanks!

@dotnetspark
Copy link
Author

@javiercn I know this issue was closed, but I noticed while using my current approach, the file won't go to the Downloads window until finished. Thus, I get no progress.
In contrast, comparing to Dropbox or Google Drive, when you click download -no matter how big the file is-, the file goes to the Downloads window, you see the progress and the UI is free.

@dotnetspark dotnetspark reopened this Jun 30, 2021
@ghost
Copy link

ghost commented Jul 1, 2021

This issue has been resolved and has not had any activity for 1 day. It will be closed for housekeeping purposes.

See our Issue Management Policies for more information.

@ghost ghost closed this as completed Jul 1, 2021
@ghost ghost locked as resolved and limited conversation to collaborators Jul 31, 2021
This issue was closed.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-blazor Includes: Blazor, Razor Components feature-blazor-server ✔️ Resolution: Answered Resolved because the question asked by the original author has been answered. Status: Resolved
Projects
None yet
Development

No branches or pull requests

3 participants