Blazor 服务器文件上传非常慢

问题描述 投票:0回答:2

更新

我暂时把这件事放在一边,但现在又回来了。我找不到任何有帮助的东西。有谁知道我可以调试 CopyToAsync 函数内部发生的情况的方法,或者我可以查看瓶颈所在的方法。从客户端机器端或网络服务器端。我担任开发人员已有一段时间了,但对 Web 开发还是新手,所以我不熟悉浏览器开发工具或任何可以帮助我找到此问题的工具。任何建议将不胜感激,因为我现在完全陷入困境。

问题

我的上传速度似乎非常慢。上传 8MB 文件大约需要 22 或 23 秒。在 LAN 内,网络服务器启动需要 2 或 3 秒。通过一些日志记录,我缩小了范围,所有时间都花在了对 Stream.CopyToAsync() 的调用上。在底部我放置了两个日志输出来显示本地和远程之间的上传差异。 CopyToAsync 之前和之后的所有其他代码都运行得很快。我已经包含了下面制作的 blazor 组件的代码。

研究

我已经检查了以下案例以及许多其他阅读材料,但似乎还没有找到任何有帮助的内容。

我应该在每个等待的操作上调用ConfigureAwait(false)吗

提高流式传输大型 (1-10 GB) 文件的速度 .Net Core

将文件上传到部署为 Azure Web 应用程序的 Blazor 服务器应用程序时速度极慢

我尝试过的

正如您在代码中看到的,我尝试向 FileStream 添加一些 FileOptions,但这对速度没有影响。我也尝试过ConfigureAwait,但这并没有提高速度,而且在第一个文件后也崩溃了。我认为这是因为这是在 UI 线程中。至少这是我从研究中得出的假设。我对编程并不陌生,但对 Web 编程尤其是 Blazor 很陌生。

其他要点

服务器和远程客户端都位于具有高速互联网的位置(同一城市内),并且网络服务器没有沉重的负载。

问题

8MB 23 秒对于代码的设计方式来说是正常的还是你认为这也太多了?

我的代码是正确的方法吗?除了速度之外,它的功能确实相当稳定。

对于尝试或研究解决此问题有什么建议吗?

代码

@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;
        }
    }
}

本地日志

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

远程日志

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
c# file stream blazor blazor-server-side
2个回答
1
投票

对我来说,将 8MB 文件上传到我的服务器只需不到一秒。我定期将 50MB 的库存照片图像上传到我的网站 - 包括处理和图像大小调整,每次平均需要几秒钟。

所以,正如您怀疑的那样,这里有问题。

乍一看,我所做的唯一明显不同的是我没有为流副本指定缓冲区长度。不过,我想您已经玩过所有这些东西了。

你说的是同城服务器,但是什么样的服务器呢?我假设您没有使用共享主机或类似的东西,对吧?在注册 Microsoft VM 之前,我使用 Godaddy 大约 2 天。


0
投票

我会使用

Parallel.ForEach
并避免异步。同步代码通常运行得更快,这是一项非常适合并行性的任务。

此外,您的

using
上缺少
OpenReadStream
。当您拥有 using 块时,您不需要手动关闭或刷新流。

一旦处于并行环境中,

userMessage =
showMessage = true;
就变得毫无意义。不要访问并行上下文之外的共享状态。

Parallel.ForEach(selectedFiles, file =>
{
    var path = $"{env.WebRootPath}\\Uploads\\{file.Name}";
    const int kb = 1024;
    const int mb = 1024 * kb;
    using FileStream fs = new(path, FileMode.Create, FileAccess.ReadWrite, FileShare.None, mb, FileOptions.Asynchronous);
    using Stream stream = file.OpenReadStream(2147483648);
    stream.CopyTo(fs, mb);
    
    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;
        OnUploadEnd.Invoke(false);
        return;
    }

    OnFileAdd.Invoke(upFile);
}
© www.soinside.com 2019 - 2024. All rights reserved.