Megabit / Blazorise

Blazorise is a component library built on top of Blazor with support for CSS frameworks like Bootstrap, Tailwind, Bulma, AntDesign, and Material.
https://blazorise.com/
Other
3.31k stars 533 forks source link

FileEdit freezes app and doesn't upload when MaxChunkSize is larger than 24*1024 #3738

Closed janseris closed 2 years ago

janseris commented 2 years ago

Describe the bug FileEdit freezes the app for a moment (shows reconnecting attempt 1 of 8 etc) and doesn't upload when MaxChunkSize is larger or equal than 24×1024. It works well when 23×1024 or below. I tried to speed up file upload because it is very slow (local Blazor app run in VS should have fast file upload I suppose) so I played with MaxChunkSize and both upload scenarios (buffered/non-buffered). It is still slow and meanwhile I discovered this issue.

Btw. is actually the non-buffered code still buffered with default buffer size 20480? Is that what MaxChunkSize does?

To Reproduce

  1. Try upload a file in the form

Expected behavior File uploads with any "chunk size"

Sample code - minimalistic repo ESDRegistrace.zip

Video (expires after 30 days): https://easyupload.io/c995oa

Blazor server Microsoft Visual Studio Community 2022 (64-bit) - Preview Version 17.2.0 Preview 4.0 image

stsrki commented 2 years ago

I didn't test it, but I suppose the reason for slowness is that you're calling StateHasChanged in the Progressed event and that will trigger page re-render. So it has to render the entire page each time something is read. I would copy the following code and make it part of a sub-component so that it refreshes only that small part of the page.

MyUpload.razor

<Addons>
  <Addon AddonType="AddonType.Start">
      <AddonLabel>
          <span class="bi bi-file-earmark-text"></span>
      </AddonLabel>
  </Addon>
  <Addon AddonType="AddonType.Body">
      <FileEdit @ref=fileEdit
                AutoReset=false
                MaxChunkSize=24*1024
                Changed=OnSelectedFileChanged
                Written=OnFilePartReceived
                Ended=OnFileUploadEnded
                Progressed=OnFileUploadProgressChanged />
  </Addon>
  @if (FileUploadProgressVisible)
  {
      <Addon AddonType="AddonType.End">
          <AddonLabel Style="cursor: progress;">
              <SpinKit Size="1rem" Type="SpinKitType.Chase" />
          </AddonLabel>
      </Addon>
  }
</Addons>
@code{
  // other events handlers
}

The use it on the form as

<Field Horizontal>
    <FieldBody>
        <MyUpload ... />
    </FieldBody>
</Field>
janseris commented 2 years ago

@stsrki So I have removed all status changes and prints etc. and 10 MB file upload still takes 2,5 seconds or 2 seconds (buffer 1 MB or non-buffered) which is slow on localhost (throughput only 4 to 5 MB/s on PC with fast SSD where I download Visual Studio 30 MB/s). Moreover, the issue with 24 * 1024 persists. The file does not upload when MaxChunksize is set to this value and it shows this (after 10 to 20 seconds for a 11 MB file):

image

Here is a project with minimalistic repo with only FileEdit in the form on page /fileUploadTest (also accessible via NavMenu). ESDRegistrace.zip

Uploading a 470 MB file on localhost with this project running on Debug configuration results in constant 30 % CPU usage (so the server would be DoSed with 10+ clients). It takes 100 seconds without explicit buffering (MaxChunkSize on FileEdit is set to 23*1024) and also with 1 MB buffering. image

on Release configuration it takes 54 s without explicit buffering (MaxChunkSize on FileEdit is set to 23*1024) and also with 1 MB buffering. CPU usage 15 %.

Throughput = only 8,7 MB/s on Release build configuration on localhost without any prints etc. during upload on SSD machine and i7 6700 HQ without any CPU or I/O intensive tasks running in background.

ESDRegistrace showing Blazorise Notifications.zip

Thanks for providing the code for a component. I will try using it to optimize speed in UI in future. It looks nice and will clean the form code.

David-Moreira commented 2 years ago

Hello @janseris

You're using Blazor Server, quote from docs:

In Blazor Server, JS interop calls are limited in size by the maximum incoming SignalR message size permitted for hub methods, which is enforced by HubOptions.MaximumReceiveMessageSize (default: 32 KB). JS to .NET SignalR messages larger than MaximumReceiveMessageSize throw an error. The framework doesn't impose a limit on the size of a SignalR message from the hub to a client.

https://docs.microsoft.com/en-us/aspnet/core/blazor/javascript-interoperability/call-javascript-from-dotnet?view=aspnetcore-6.0#size-limits-on-javascript-interop-calls

So this explains your problem with the MaxChunkSize which basically transfers more information through JsInterop as you increase it, and certainly you've exceeded the default.

As for the reported slowness we can only try to keep improving upon it, we'll be taking a look at it and do some comparison tests vs native Blazor FileInput.

For the time being try playing with different MaxChunkSize values, my guess is that a higher value might actually be better because it cuts down on the number of js interop calls. Matter of testing if you don't mind. :)

github-actions[bot] commented 2 years ago

This is an automated message reminding that this issue is expecting the author's answer.