You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Blazor Server日志页面加载延迟与文件读取性能优化咨询

Blazor Server大日志文件加载优化方案

问题场景

现有Blazor Server应用,其中一个Razor页面展示服务器上2MB、约3万行的大日志文件,点击按钮可刷新内容。存在两个问题:

  1. 首次点击刷新按钮需4-5秒才开始显示内容;
  2. 离开页面后再次返回,页面需等待4-5秒才打开,推测是重新加载/渲染所有日志行导致。

需求

  1. 实现页面立即打开并显示加载动画,后台加载日志,完成后再展示内容;
  2. 确认2MB日志读取耗时4-5秒是否正常,提供代码优化方案缩短耗时。

用户现有代码

Razor页面代码

<body>
<table class="page_header">
    <tr class="tr_button_bar">
        <td class="td_button_bar" align="left">
            <button class="button_1"  @onclick="Read_Error_Log"><img class="button_image" src="images/read_data.svg"><br>Error Log</button>           
       </td>
    </tr>
</table>
<br>  
<style>
    .preformatted {
        white-space: pre;
        max-width:1600px;           
        margin: 0;
        line-height: 1;
    }
</style>

@if (TagService.Loading_Main_Log == true)
{
    <div class="spinner"></div>
}

<table class="log_file_table">
    @for (int f = 0; f < TagService.Error_log.Count; f++)
    {
        <tr style="max-height:20px !important">
            <td class=".log_file"><pre class="preformatted">@TagService.Error_log[f]</pre></td>
        </tr>
    }
</table>
</body>

Razor.cs代码

public partial class Logs
{
    public async Task Read_Error_Log()
    {
        TagService.Loading_Main_Log = true;
        await Task.Delay(1);
        try
        {
            TagService.Error_log.Clear();
                Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
                var lines = File.ReadLines(CommonClass.error_path, Encoding.GetEncoding("Windows-1254"));
                var line_trimmed = "";

                foreach (var line in lines)
                {
                    TagService.Error_log.Add(line);
                }               
        }
        catch (Exception e)
        {
            using (StreamWriter sw = File.AppendText(CommonClass.error_path))
            {
                sw.WriteLine("Read_Error_Log() Error " + e + " " + Convert.ToString(DateTime.Now));
            }
        }
        TagService.Loading_Main_Log = false;
    }        
}

页面导航代码

<NavLink class="nav-link" href="logs" Match="NavLinkMatch.All">
<span class="oi oi-home" aria-hidden="true"></span> Error Log

解决方案

一、实现页面立即打开+加载动画

核心问题:原代码一次性渲染3万行DOM,Blazor Server需在服务端生成所有DOM再发送到客户端,导致页面打开阻塞。优化方案如下:

1. 改用虚拟滚动组件减少初始DOM渲染

使用Blazor内置的Virtualize组件,仅渲染当前可视区域的日志行,初始渲染行数从3万降到几十行,页面瞬间打开。

2. 页面初始化异步加载日志

在页面初始化时触发日志加载,同时显示加载动画,避免白屏等待。

修改后的Razor页面代码:

@page "/logs"
@inject TagService TagService

<table class="page_header">
    <tr class="tr_button_bar">
        <td class="td_button_bar" align="left">
            <button class="button_1" @onclick="Read_Error_Log">
                <img class="button_image" src="images/read_data.svg"><br>Error Log
            </button>           
       </td>
    </tr>
</table>
<br>  

<style>
    .preformatted {
        white-space: pre;
        max-width:1600px;           
        margin: 0;
        line-height: 1;
    }
    .spinner {
        border: 4px solid rgba(0,0,0,0.1);
        width: 36px;
        height: 36px;
        border-radius: 50%;
        border-left-color: #09f;
        animation: spin 1s linear infinite;
        margin: 20px auto;
    }
    @keyframes spin {
        0% { transform: rotate(0deg); }
        100% { transform: rotate(360deg); }
    }
</style>

@if (TagService.Loading_Main_Log)
{
    <div class="spinner"></div>
}

<!-- 虚拟滚动组件,仅渲染可视区域行 -->
<Virtualize Items="@TagService.Error_log" Context="line" ItemSize="20">
    <tr>
        <td class="log_file"><pre class="preformatted">@line</pre></td>
    </tr>
</Virtualize>

二、优化日志读取速度

耗时判断:2MB日志读取4-5秒完全不正常,正常耗时应在100ms以内。原代码的问题点:

  1. 每次读取重复注册编码提供者,冗余操作;
  2. 使用同步File.ReadLines阻塞UI线程;
  3. 逐行添加到共享列表,频繁触发UI更新(若使用ObservableCollection)。

优化后的Razor.cs代码:

public partial class Logs
{
    [Inject]
    public TagService TagService { get; set; }

    // 页面初始化时自动加载日志
    protected override async Task OnInitializedAsync()
    {
        await Read_Error_Log();
    }

    public async Task Read_Error_Log()
    {
        TagService.Loading_Main_Log = true;
        StateHasChanged(); // 立即通知UI显示加载动画
        
        try
        {
            // 本地缓存所有行,避免频繁更新共享列表
            var localLines = new List<string>();
            
            // 异步读取文件,不阻塞UI线程
            await using var stream = new FileStream(
                CommonClass.error_path, 
                FileMode.Open, 
                FileAccess.Read, 
                FileShare.ReadWrite // 允许其他进程写入文件时读取
            );
            using var reader = new StreamReader(stream, Encoding.GetEncoding("Windows-1254"));
            
            string line;
            while ((line = await reader.ReadLineAsync()) != null)
            {
                localLines.Add(line);
            }

            // 批量更新共享列表,减少UI更新次数
            TagService.Error_log.Clear();
            TagService.Error_log.AddRange(localLines);
        }
        catch (Exception e)
        {
            await using var sw = File.AppendText(CommonClass.error_path);
            await sw.WriteLineAsync($"Read_Error_Log() Error {e} {DateTime.Now}");
        }
        finally
        {
            TagService.Loading_Main_Log = false;
            StateHasChanged(); // 通知UI隐藏加载动画
        }
    }        
}

额外优化:

  1. 编码注册移到启动时:在Program.cs中添加Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);,仅执行一次;
  2. 缓存日志内容:若日志不是实时更新,可添加缓存逻辑(如设置5分钟有效期),避免重复读取;
  3. 线程安全:若TagService是单例,需确保Error_log的操作线程安全(可加锁或使用线程安全集合)。

内容的提问来源于stack exchange,提问作者Mdarende

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 07:00:54