Blazor - Upload File Progressly

Blazor Server App, UploadFile, ProgressBar,上傳檔案可看到進度條

引言

上傳檔案過程可以看到進度條。

Using Blazor Server-side, is it possible to get file progress upload status from from the controller to the client on large files?

參考文章

開發平台

平台: .NET8 語言: C# 框架: Blazor Server App CSS: MudBlazor

關鍵程式碼

可視的上傳檔案元件。前置條件:已拿到 IBorwserFile 了。

UploadFileProgressly.razor
@inject IDialogServiceEx dlgSvc

@if (Loading)
{
  <MudProgressLinear Color=Color.Info Class="my-2" Value=valuePercent Indeterminate=isUploaded Striped=isUploaded>
    <MudText Typo=Typo.body2 Style="background:#0007; color:#EEE; padding:.125em .25em; border-radius:.25em;">
      @($"正在上傳: {fileName} ({valuePercent:N1}%)")
    </MudText>
  </MudProgressLinear>
}

@code {
  [Parameter] public bool Loading { get; set; }

  //## State
  int fileSize = 0;
  int fileReadPosition = 0;
  string fileName = string.Empty;

  //## Property
  double valuePercent => (double)fileReadPosition * 100d / (double)fileSize;
  bool isUploaded => valuePercent >= 100;

  /// 公開指令
  public async Task<MemoryStream> StartUploadFileAsync(IBrowserFile file, long maxAllowedSize)
  {
    try
    {
      fileName = file.Name;
      fileSize = (int)file.Size;
      fileReadPosition = 0;
      await InvokeAsync(StateHasChanged); // 通知畫面更新進度

      // 開始上傳-顯示進度
      using var fs = file.OpenReadStream(maxAllowedSize);
      MemoryStream ms = new MemoryStream(fileSize);

      byte[] buffer = new byte[32 * 1024]; // 32KB buffer
      int bytesRead;
      while ((bytesRead = await fs.ReadAsync(buffer, 0, buffer.Length)) > 0)
      {
        await ms.WriteAsync(buffer, 0, bytesRead);
        fileReadPosition += bytesRead;
        await InvokeAsync(StateHasChanged); // 通知畫面更新進度
      }

      return ms;
    }
    catch (Exception ex)
    {
      throw new ApplicationException($"上傳檔案[{file.Name}]失敗!", ex);
    }
  }

  public void Reset()
  {
    fileName = string.Empty;
    fileSize = 0;
    fileReadPosition = 0;
  }
}

應用

@inject YourOperationBiz bizSvc
@inject IDialogServiceEx dlgSvc
@inject ISnackbar snackSvc

...略...

<MudButton OnClick=HandleUploadFile>
  上傳檔案
</MudButton>

@* 可視的上傳檔案元件 *@
<UploadFileProgressly Loading=f_uploading @ref=refUploadFile />

@code {
  //@bind-UploadFileList 欲上傳的檔案清單
  [Parameter] public List<IBrowserFile> UploadFileList { get; set; }
  [Parameter] public EventCallback<List<IBrowserFile>> UploadFileListChanged { get; set; }
  // event, 當檔案清單全部上傳完畢,上通知去刷新畫面
  [Parameter] public EventCallback OnFilesUploaded { get; set; }

  //## Resource
  UploadFileProgressly refUploadFile;
  const long MAX_ALLOWED_SIZE = 30 * 1024 * 1024; // 30MB, 預設:512KB

  //## State
  bool f_uploading = false;

  async Task HandleUploadFile()
  {
    try
    {
      f_uploading = true;

      // 依選好的檔案清單,一個一個上傳檔案
      while (UploadFileList.Count > 0)
      {
        // 拿出一個檔案並上傳。
        var file = UploadFileList.ElementAt(0);
        UploadFileList.RemoveAt(0);
        await UploadFileListChanged.InvokeAsync(UploadFileList);

        // 開始上傳
        if (file.Size > MAX_ALLOWED_SIZE)
        {
          snackSvc.Add($"檔案[{file.Name}]超過 30MB 不可上傳!", Severity.Error);
          continue;
        }

        //※ 開始上傳-顯示上傳進度
        using var ms = await refUploadFile.StartUploadFileAsync(file, MAX_ALLOWED_SIZE);
        await bizSvc.UploadFileAsync(ms.ToArray(), file.Name);

        snackSvc.Add($"上傳檔案[{file.Name}]成功。", Severity.Success);
        await Task.Delay(500);
      }

      // 已完成,通知去刷新畫面
      refUploadFile.Reset();
      await OnFilesUploaded.InvokeAsync();
    }
    catch (Exception ex)
    {
      dlgSvc.ShowAlert("出現例外!" + ex.Message, "出現例外!");
    }
    finally
    {
      f_uploading = false;
    }
  }
}

沒圖沒真象

(EOF)

Last updated