这是Blazor上传文件的最佳方式吗?

Blazor不得不说真是好东西,极大的提升了开发效率,很多的页面交互功能基本上只需要写很少的代码就能实现了,而且还是无js实现,你也绝对没有想到过,Blazor实现文件上传是有多么简单!.

先说结论:Blazor实现带进度显示的文件上传真的很简单!效果看图:

这是Blazor上传文件的最佳方式吗?

实现这么一个小功能,仅仅只花了不到50行的代码就实现了,接下来就给大家分享下案例实现吧。

首先引入Tewr.Blazor.FileReader包,这个包能够提供文件上传的流式读取,这样便可以实现在服务端对上传文件进行一边上传一遍写文件的操作。

配置依赖注入(站长注:这是Blazor Server模式,wasm方式请查看文末仓库文档说明):

services.AddFileReaderService();

接下来我们先进行页面布局,很简单,再声明两个变量用于显示进度和显示图片:

<input type="file"/><button>上传文件</button>
<div>
    @if (!string.IsNullOrEmpty(_src))
    {
        <img src="@_src" width="600px" />
    }
    else
    {
        <p>@progress</p>
    }
</div>

然后在组件中注入IFileReaderService服务

@using Tewr.Blazor.FileReader
@inject IFileReaderService fileReaderService;

为了让文件框能够和C#代码进行交互,所以需要将它通过ElementReference引用起来:

<input @ref=inputTypeFileElement type="file" /><button>上传文件</button>
<div>
    @if (!string.IsNullOrEmpty(_src))
    {
        <img src="@_src" width="600px" />
    }
    else
    {
        <p>@progress</p>
    }
</div>
@code {
    private ElementReference inputTypeFileElement;
    private string _src;
    private string progress;
}

给按钮绑定事件,按钮触发后通过fileReaderService进行文件流的读取,接下来便是常规的二进制数据copy操作,可以拿到文件的传输进度,计算之后便能显示到页面中

<button @onclick=ReadFile>上传文件</button>
public async Task ReadFile()
{
    _src = "";
    foreach (var file in await fileReaderService.CreateReference(inputTypeFileElement).EnumerateFilesAsync())
    {
        await using var fileStream = await file.OpenReadAsync();
        var buffer = new byte[2048];
        var finalBuffer = new byte[fileStream.Length];
        int count;
        int totalCount = 0;
        while ((count = await fileStream.ReadAsync(buffer, 0, buffer.Length)) != 0)
        {
            Buffer.BlockCopy(buffer, 0, finalBuffer, totalCount, count);
            totalCount += count;
            progress = "文件上传中 " + (int)(totalCount * 100.0 / fileStream.Length) + "%";
            StateHasChanged();
        }
        _src = $"data:image/jpg;base64,{Convert.ToBase64String(finalBuffer)}";
        progress = "";
        StateHasChanged();
    }
}

完整代码如下:

@page "/counter"
@using Tewr.Blazor.FileReader
@inject IFileReaderService fileReaderService;
 
<input @ref=inputTypeFileElement type="file" />
<button @onclick=ReadFile>上传文件</button>
<div>
    @if (!string.IsNullOrEmpty(_src))
    {
        <img src="@_src" width="600px" />
    }
    else
    {
        <p>@progress</p>
    }
</div>
 
@code {
    private ElementReference inputTypeFileElement;
    private string _src;
    private string progress;
 
    public async Task ReadFile()
    {
        _src = "";
        foreach (var file in await fileReaderService.CreateReference(inputTypeFileElement).EnumerateFilesAsync())
        {
            await using var fileStream = await file.OpenReadAsync();
            var buffer = new byte[2048];
            var finalBuffer = new byte[fileStream.Length];
            int count;
            int totalCount = 0;
            while ((count = await fileStream.ReadAsync(buffer, 0, buffer.Length)) != 0)
            {
                Buffer.BlockCopy(buffer, 0, finalBuffer, totalCount, count);
                totalCount += count;
                progress = "文件上传中 " + (int)(totalCount * 100.0 / fileStream.Length) + "%";
                StateHasChanged();
            }
            _src = $"data:image/jpg;base64,{Convert.ToBase64String(finalBuffer)}";
            progress = "";
            StateHasChanged();
        }
    }
}

站长插播:

文章首图演示的是一张不到1MB的图片,因为Tewr.Blazor.FileReader这个包提供文件上传的流式读取,上传大文件也是可以的,下面这是上传一个34.2MB的ZIP压缩包,Blazor服务端模式:

这是Blazor上传文件的最佳方式吗?

 

demo做的一般,可能gif看不出啥,只是为了证明这个包确实不错,要实现大文件上传,可把上面单包读取大小改大一点,比如:512KB:

var buffer = new byte[1024*512];

如果看下方微软Blazor文件上传文档,把单包大小改成大于20KB,页面可能会卡一下,然后页面自动刷新就把上传操作给重置了,而使用这个包确没这个问题,这个包很nice。