1

Update

I set this aside for a bit but I'm back on it now. I haven't been able to find anything that helps. Does anyone know a way I can debug what is happening inside the CopyToAsync function or a way I can see where the bottle neck is. Either from the client machine end or the web server end. I've been a developer for some time but am new to web development so I'm not familiar with the browser dev tools or anything that could help me locate this issue. Any suggestions would be appreciated as I'm completely stuck at this point.

Problem

I'm experiencing what seems to be very slow upload speeds. About 22 or 23 seconds to upload an 8MB file. Within the LAN the webserver is on it takes 2 or 3 seconds. Through some logging I've narrowed it down that all the time is spent in my call to Stream.CopyToAsync(). At the bottom I put the two log outputs to show the upload difference between local and remote. All the other code before and after the CopyToAsync runs fast. I've included the code for the blazor component I made below.

Research

I've checked the following cases plus lots of other reading but haven't seemed to find anything yet that has helped.

Should I call ConfigureAwait(false) on every awaited operation

Increase Speed for Streaming Large(1-10 gb) files .Net Core

Extremely Slow file upload to a Blazor Server app deployed as Azure Web App

What I've Tried

As you can see in the code I tried adding adding some FileOptions to the FileStream but that had no effect on the speed. I've also tried ConfigureAwait but that didn't improve speed and also crashed it after the first file. I think that is because this is in the UI thread. At least that's what I assumed from my research. I'm not new to programming but I am new to web programming and especially Blazor.

Other Points

Both server and remote client are in locations with high speed internet (within the same city) and the web server does not have a heavy load on it.

Questions

Is 23 seconds for 8MB normal for the way the code is designed or do you think this is excessive too?

Is my code the correct approach? It does function pretty solid except for the speed.

Any suggestions on things to try or to research to fix this?

Code

@using System.IO
@inject SessionService session
@inject Microsoft.AspNetCore.Hosting.IWebHostEnvironment env
@inject IJSRuntime JSRuntime

@if (showMessage)
{
    <div style="font-size: 20px;" class="alert-danger border-danger">
        <p>@userMessage</p>
    </div>
}

<button class="btn btn-primary m-2" onclick="document.getElementById('filepicker').click()" disabled="@disabled">@buttonText</button>
<InputFile id="filepicker" OnChange="@OnInputFileChange" hidden multiple="@multipleFiles" />

@code
{
    [Parameter] public EventCallback<FileModel> OnFileAdd { get; set; }
    [Parameter] public EventCallback<int> OnUploadStart { get; set; }
    [Parameter] public EventCallback<bool> OnUploadEnd { get; set; }
    [Parameter] public string buttonText { get; set; } = "Upload";
    [Parameter] public bool multipleFiles { get; set; } = false;
    [Parameter] public int callBackRefID { get; set; } = 0;
    private IReadOnlyList<IBrowserFile> selectedFiles;
    private bool showMessage = false;
    private bool disabled = false;
    private MarkupString userMessage;

    private async Task OnInputFileChange(InputFileChangeEventArgs e)
    {
        if (e.FileCount > 0)
        {
            disabled = true;
            await JSRuntime.InvokeAsync<string>("console.log", "Starting " + e.FileCount + " files - " + DateTime.Now.ToString());
            await OnUploadStart.InvokeAsync(e.FileCount);
            selectedFiles = e.GetMultipleFiles(e.FileCount);

            foreach (var file in selectedFiles)
            {
                await JSRuntime.InvokeAsync<string>("console.log", "Starting " + file.Name + " - " + DateTime.Now.ToString());
                Stream stream = file.OpenReadStream((long)2147483648);
                var path = $"{env.WebRootPath}\\Uploads\\{file.Name}";

                //FileOptions had no effect on performance
                FileStream fs = File.Create(path, 1048576, FileOptions.Asynchronous | FileOptions.SequentialScan);
                //FileStream fs = File.Create(path, 1048576);

                await JSRuntime.InvokeAsync<string>("console.log", "Before stream.CopyToAsync - " + DateTime.Now.ToString());
                await stream.CopyToAsync(fs, 1048576);
                //ConfigureAwait causes crash after first file is done.
                //await stream.CopyToAsync(fs, 1048576).ConfigureAwait(false);
                await JSRuntime.InvokeAsync<string>("console.log", "After stream.CopyToAsync - " + DateTime.Now.ToString());

                stream.Close();
                fs.Close();

                FileModel upFile = new();
                upFile.S_ID = session.CurrentUser.ID;
                upFile.Name = file.Name;
                upFile.DisplayName = "";
                upFile.Descr = "";
                upFile.ContentType = file.ContentType;

                if (upFile.GetFileData(path, file.ContentType) == false)
                {
                    userMessage = new(upFile.ErrMsg);
                    showMessage = true;
                    await OnUploadEnd.InvokeAsync(false);
                    return;
                }

                await OnFileAdd.InvokeAsync(upFile);
                await JSRuntime.InvokeAsync<string>("console.log", "Finished " + file.Name + " - " + DateTime.Now.ToString());
            }

            await OnUploadEnd.InvokeAsync(true);
            await JSRuntime.InvokeAsync<string>("console.log", "Finished - " + DateTime.Now.ToString());
            disabled = false;
        }
    }
}

Local Log

blazor.server.js:1 Starting 3 files - 2021-04-12 2:01:33 PM

blazor.server.js:1 Starting IMG_7830.JPG - 2021-04-12 2:01:33 PM
blazor.server.js:1 Before stream.CopyToAsync - 2021-04-12 2:01:33 PM
blazor.server.js:1 After stream.CopyToAsync - 2021-04-12 2:01:36 PM
blazor.server.js:1 Finished IMG_7830.JPG - 2021-04-12 2:01:37 PM

blazor.server.js:1 Starting IMG_7831.JPG - 2021-04-12 2:01:37 PM
blazor.server.js:1 Before stream.CopyToAsync - 2021-04-12 2:01:37 PM
blazor.server.js:1 After stream.CopyToAsync - 2021-04-12 2:01:40 PM
blazor.server.js:1 Finished IMG_7831.JPG - 2021-04-12 2:01:41 PM

blazor.server.js:1 Starting IMG_7832.JPG - 2021-04-12 2:01:41 PM
blazor.server.js:1 Before stream.CopyToAsync - 2021-04-12 2:01:41 PM
blazor.server.js:1 After stream.CopyToAsync - 2021-04-12 2:01:43 PM
blazor.server.js:1 Finished IMG_7832.JPG - 2021-04-12 2:01:44 PM

blazor.server.js:1 Finished - 2021-04-12 2:01:44 PM

Remote Log

blazor.server.js:1 Starting 3 files - 2021-04-12 12:01:44 PM

blazor.server.js:1 Starting IMG_7830.JPG - 2021-04-12 12:01:45 PM
blazor.server.js:1 Before stream.CopyToAsync - 2021-04-12 12:01:45 PM
blazor.server.js:1 After stream.CopyToAsync - 2021-04-12 12:02:07 PM
blazor.server.js:1 Finished IMG_7830.JPG - 2021-04-12 12:02:08 PM

blazor.server.js:1 Starting IMG_7831.JPG - 2021-04-12 12:02:08 PM
blazor.server.js:1 Before stream.CopyToAsync - 2021-04-12 12:02:08 PM
blazor.server.js:1 After stream.CopyToAsync - 2021-04-12 12:02:30 PM
blazor.server.js:1 Finished IMG_7831.JPG - 2021-04-12 12:02:31 PM

blazor.server.js:1 Starting IMG_7832.JPG - 2021-04-12 12:02:31 PM
blazor.server.js:1 Before stream.CopyToAsync - 2021-04-12 12:02:31 PM
blazor.server.js:1 After stream.CopyToAsync - 2021-04-12 12:02:53 PM
blazor.server.js:1 Finished IMG_7832.JPG - 2021-04-12 12:02:54 PM

blazor.server.js:1 Finished - 2021-04-12 12:02:54 PM

1 Answers1

1

For me, an 8MB file upload to my server would take well less than one second. I regularly upload 50MB stock photo images to my site-- INCLUDING processing and image resizing, each averages a few seconds.

So, as you suspect, there's a problem here.

At a glance, the only thing substantially different I did was that I didn't specify a buffer length for the stream copy. I suppose you've already played around with all that stuff, though.

You talk about the server being in the same city, but what kind of server? I'm assuming you aren't using shared hosting or anything like that, right? I used Godaddy for about 2 days before I signed up for a Microsoft VM.

Bennyboy1973
  • 3,413
  • 2
  • 11
  • 16
  • My process also does a resize to 2 different sizes and saves to a database. All that is done in 1 second. The buffer was the first thing I tried. I just tried removing the specified size though and same result. The server is a physical server at my office. I don't have the specs but I don't suspect that's the problem as the server is very lightly loaded. – Mark Grecco Apr 12 '21 at 21:08