Enterprise-level component library based on Bootstrap and Blazor
gitee
version
license
download
repo
commit
build
coverage

Upload

Upload the file by clicking

The InputUpload component is used with other form components to display the file name, select the file and upload it by clicking the browse button, and by setting the ShowRemoveButton parameter, display the delete button, click the delete button to call back onDelete delegate method

Demo

Use the file upload component to constrain the file format within the form

Demo
  • Using the ValidateForm form component, custom validation is set by setting the fileValidation label of the model properties to support file extensionsizevalidation, in this case with the extension .png .jpg .jpeg and the file size limit to 50K
  • After selecting the file and not starting to upload the file, click the submit button to verify that the data is legitimate, and then upload the file OnSubmit callback delegate, noting that the Pictureproperty type is IBrowserFile

The ButtonUpload components, classic styles, user click button to pop up the file selection box.

Demo

Click on the browse button select file upload, in this case set IsMultiple-true multiple-selectable file can be uploaded

When you set up IsSingle, you can upload only one image or file

Use DefaultFileList to set up uploaded content

Demo
Test.xls (0 B)
Test.doc (0 B)
Test.ppt (0 B)
Test.mp3 (0 B)
Test.mp4 (0 B)
Test.pdf (0 B)
Test.cs (0 B)
Test.zip (0 B)
Test.txt (0 B)
Test.dat (0 B)

Use DefaultFileList to set up uploaded content

Demo

AvatarUpload component, using the OnChange to limit the format and size of images uploaded by users. In this example, only jpg/png/bmp/jpeg/gif five picture formats are allowed

Demo

Card form avatar box

Round avatar frame

When you set up IsSingle, you can upload only one image or file

RELATED: [Accept] < a href='http://www.iana.org/assignments/media-types/media-types.xhtml' target='_blank' >[Media Types]

Set the preview address PrevUrl with the DefaultFileList property

Verify that an example of using a picture box is used in the form

CardUpload components and rendered in card-style band preview mode

Demo

SSR mode
Server Side mode, you can use the IWebHostEnvironment injection service to get to the wwwroot directory and save the file to the images/uploader, which does not require a direct call the controller secondary of MVCSaveToFile method
Wasm mode
It wasn't available in wasm mode, IWebHostEnvironment needed to save files to the server side by calling webapi interface, and so on
Interested students can view their knowledge of Upload components through the wiki in the open source repository related resources of the [The portal]
In this example, server-side verification prompts the file for too much prompt when the file size exceeds 200MB

In this example, the ShowProgress=true display upload progress bar

When you set up IsSingle, you can upload only one image or file

Icons are displayed in different file formats

Demo
Test.xls (0 B)
Test.doc (0 B)
Test.ppt (0 B)
Test.mp3 (0 B)
Test.mp4 (0 B)
Test.pdf (0 B)
Test.cs (0 B)
Test.zip (0 B)
Test.txt (0 B)
Test.dat (0 B)

use data:image/xxx;base64,XXXXX format data string as PrevUrl value

Demo
Test (0 B)

InputUpload

Loading...

ButtonUpload/CardUpload

Loading...

AvatarUpload

Loading...
@page "/uploads"

<h3>Upload</h3>

<h4>Upload the file by clicking</h4>

<DemoBlock Title="Basic usage" Introduction="The <code>InputUpload</code> component is used with other form components to display the file name, select the file and upload it by clicking the <b>browse button</b>, and by setting the <code>ShowRemoveButton</code> parameter, display the <b>delete button</b>, click the delete button to call back <code>onDelete</code> delegate method" Name="Normal">
    <div class="row g-3">
        <div class="col-12 col-sm-6">
            <label for="text1" class="form-label">Name:</label>
            <input id="text1" class="form-control">
        </div>
        <div class="col-12 col-sm-6">
            <label for="text2" class="form-label">Adress:</label>
            <input id="text2" class="form-control">
        </div>
        <div class="col-12">
            <label for="text3" class="form-label">Photo:</label>
            <InputUpload TValue="string" ShowDeleteButton="true" OnChange="@OnFileChange" OnDelete="@OnFileDelete"></InputUpload>
        </div>
    </div>
    <BlockLogger @ref="Trace" class="mt-3" />
</DemoBlock>

<DemoBlock Title="FormSettings" Introduction="Use the file upload component to constrain the file format within the form" Name="FormSettings">
    <ul class="ul-demo mb-3">
        <li>Using the <code>ValidateForm</code> form component, custom validation is set by setting the <code>fileValidation</code> label of the model properties to support file <b>extension</b><b>size</b>validation, in this case with the extension <code>.png .jpg .jpeg</code> and the file size limit to <code>50K</code></li>
        <li>After selecting the file and not starting to upload the file, click the <code>submit</code> button to verify that the data is legitimate, and then upload the file <code>OnSubmit</code> callback delegate, noting that the <b>Picture</b>property type is <code>IBrowserFile</code></li>
    </ul>
    <ValidateForm Model="Foo" OnValidSubmit="OnSubmit">
        <div class="row g-3">
            <div class="col-12">
                <BootstrapInput @bind-Value="@Foo.Name" />
            </div>
            <div class="col-12">
                <InputUpload @bind-Value="@Foo.Picture" />
            </div>
            <div class="col-12">
                <Button ButtonType="@ButtonType.Submit" Text="Submit"></Button>
            </div>
        </div>
    </ValidateForm>
</DemoBlock>

<DemoBlock Title="Click upload" Introduction="The <code>ButtonUpload</code> components, classic styles, user click button to pop up the file selection box." Name="ClickUpload">
    <div class="row g-3">
        <div class="col-12 col-sm-6">
            <p>Click on the <code>browse button</code> select file upload, in this case set <code>IsMultiple-true</code> multiple-selectable file can be uploaded</p>
            <ButtonUpload TValue="string" IsMultiple="true" ShowProgress="true" OnChange="@OnClickToUpload" OnDelete="@(fileName => Task.FromResult(true))"></ButtonUpload>
        </div>
    </div>
    <p class="mt-3">When you set up <code>IsSingle</code>, you can upload only one image or file</p>
    <ButtonUpload TValue="string" IsSingle="true" OnChange="@OnClickToUpload" OnDelete="@(fileName => Task.FromResult(true))" class="mt-3"></ButtonUpload>
</DemoBlock>

<DemoBlock Title="A list of files has been uploaded" Introduction="Use <code>DefaultFileList</code> to set up uploaded content" Name="UploadedFiles">
    <div class="row g-3">
        <div class="col-12 col-sm-6">
            <ButtonUpload TValue="string" ShowDownloadButton="true" OnDownload="OnDownload" OnDelete="@(fileName => Task.FromResult(true))" DefaultFileList="@DefaultFormatFileList"></ButtonUpload>
        </div>
    </div>
</DemoBlock>

<DemoBlock Title="Upload a folder" Introduction="Use <code>DefaultFileList</code> to set up uploaded content" Name="UploadFolder">
    <div class="row g-3">
        <div class="col-12 col-sm-6">
            <ButtonUpload TValue="string" IsDirectory="true" OnChange="@OnUploadFolder" OnDelete="@(fileName => Task.FromResult(true))"></ButtonUpload>
        </div>
    </div>
</DemoBlock>

<DemoBlock Title="User profile picture upload" Introduction="<code>AvatarUpload</code> component, using the <code>OnChange</code> to limit the format and size of images uploaded by users. In this example, only <code>jpg/png/bmp/jpeg/gif</code> five picture formats are allowed" Name="AvatarUpload">
    <div class="row g-3">
        <div class="col-12">
            <p>Card form avatar box</p>
            <AvatarUpload TValue="string" Accept="image/*" OnChange="@OnAvatarUpload" OnDelete="@(fileName => Task.FromResult(true))"></AvatarUpload>
        </div>
        <div class="col-12">
            <p>Round avatar frame</p>
            <AvatarUpload TValue="string" Accept="image/*" ShowProgress="true" IsCircle="true" OnChange="@OnAvatarUpload" OnDelete="@(fileName => Task.FromResult(true))"></AvatarUpload>
        </div>
        <div class="col-12">
            <p>When you set up <code>IsSingle</code>, you can upload only one image or file</p>
            <AvatarUpload TValue="string" IsSingle="true" OnChange="@OnAvatarUpload" OnDelete="@(fileName => Task.FromResult(true))"></AvatarUpload>
            <p>RELATED: <a href='https://www.runoob.com/tags/att-input-accept.html' target='_blank'>[Accept]</a>  < a href='http://www.iana.org/assignments/media-types/media-types.xhtml' target='_blank' >[Media Types]</a></p>
            <p></p>
            <AvatarUpload TValue="string" Accept="image/gif, image/jpeg" IsSingle="true" OnChange="@OnAvatarUpload" OnDelete="@(fileName => Task.FromResult(true))"></AvatarUpload>
        </div>
        <div class="col-12">
            <p>Set the preview address <code>PrevUrl</code> with the <code>DefaultFileList</code> property</p>
            <AvatarUpload TValue="string" Accept="image/gif, image/jpeg" IsSingle="true" DefaultFileList="@PreviewFileList"
                          OnChange="@OnAvatarUpload" OnDelete="@(fileName => Task.FromResult(true))" />
        </div>
        <div class="col-12">
            <p>Verify that an example of using a picture box is used in the form</p>
            <ValidateForm Model="Foo" OnValidSubmit="OnAvatarValidSubmit">
                <div class="row g-3">
                    <div class="col-12">
                        <BootstrapInput @bind-Value="@Foo.Name" />
                    </div>
                    <div class="col-12">
                        <AvatarUpload @bind-Value="@Foo.Picture" IsSingle="true" />
                    </div>
                    <div class="col-12">
                        <Button ButtonType="@ButtonType.Submit" Text="Submit"></Button>
                    </div>
                </div>
            </ValidateForm>
        </div>
    </div>
    <BlockLogger @ref="AvatarTrace" class="mt-3" />
</DemoBlock>

<DemoBlock Title="Preview the card style" Introduction="<code>CardUpload</code> components and rendered in card-style band preview mode" Name="PreCardStyle">
    <p>
        <div><b>SSR mode </b></div>
        <div><code>Server Side</code> mode, you can use the <code>IWebHostEnvironment</code> injection service to get to the <code>wwwroot</code> directory and save the file to the <code>images/uploader</code>, which does not require a direct call the controller secondary of <b> MVC</b><code>SaveToFile</code> method</div>

        <div><b>Wasm mode </b></div>
        <div>It wasn't available in <code>wasm</code> mode, <code>IWebHostEnvironment</code> needed to save files to the server side by calling <code>webapi</code> interface, and so on</div>
        <div>@((MarkupString)Localizer["PreCardStyleLink", SiteOptions.CurrentValue.VideoLibUrl].Value)</div>
        <div>In this example, server-side verification prompts the file for too much prompt when the file size exceeds <code>200MB</code></div>
    </p>
    <div class="row g-3">
        <div class="col-12">
            <p>In this example, the <code>ShowProgress=true</code> display upload progress bar</p>
            <CardUpload TValue="string" ShowProgress="true" OnChange="@OnCardUpload" OnDelete="@(fileName => Task.FromResult(true))"></CardUpload>
        </div>
        <div class="col-12">
            <p>When you set up <code>IsSingle</code>, you can upload only one image or file</p>
            <CardUpload TValue="string" IsSingle="true" OnChange="@OnCardUpload" OnDelete="@(fileName => Task.FromResult(true))"></CardUpload>
        </div>
    </div>
</DemoBlock>

<DemoBlock Title="The file icon" Introduction="Icons are displayed in different file formats" Name="FileIcon">
    <CardUpload TValue="string" ShowDownloadButton="true" DefaultFileList="@DefaultFormatFileList" OnChange="@OnCardUpload" OnDelete="@(fileName => Task.FromResult(true))"></CardUpload>
</DemoBlock>

<DemoBlock Title="Base64 format" Introduction="use <code>data:image/xxx;base64,XXXXX</code> format data string as PrevUrl value" Name="Base64">
    <CardUpload TValue="string" DefaultFileList="@Base64FormatFileList" IsSingle="true" />
</DemoBlock>

<AttributeTable Items="@GetInputAttributes()" Title="InputUpload" />

<AttributeTable Items="@GetButtonAttributes()" Title="ButtonUpload/CardUpload" />

<AttributeTable Items="@GetAvatarAttributes()" Title="AvatarUpload" />
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/

using BootstrapBlazor.Components;
using BootstrapBlazor.Shared.Common;
using BootstrapBlazor.Shared.Components;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Options;
using System.ComponentModel.DataAnnotations;

namespace BootstrapBlazor.Shared.Samples;

/// <summary>
/// 
/// </summary>
public sealed partial class Uploads : IDisposable
{
    private static readonly Random random = new();

    [Inject]
    [NotNull]
    private ToastService? ToastService { get; set; }

    [Inject]
    [NotNull]
    private IOptionsMonitor<WebsiteOptions>? SiteOptions { get; set; }

    [Inject]
    [NotNull]
    private IStringLocalizer<Uploads>? Localizer { get; set; }

    private List<UploadFile> PreviewFileList { get; } = new(new[] { new UploadFile { PrevUrl = "_content/BootstrapBlazor.Shared/images/Argo.png" } });

    private BlockLogger? Trace { get; set; }

    private List<UploadFile> DefaultFormatFileList { get; } = new List<UploadFile>()
    {
        new UploadFile { FileName = "Test.xls" },
        new UploadFile { FileName = "Test.doc" },
        new UploadFile { FileName = "Test.ppt" },
        new UploadFile { FileName = "Test.mp3" },
        new UploadFile { FileName = "Test.mp4" },
        new UploadFile { FileName = "Test.pdf" },
        new UploadFile { FileName = "Test.cs" },
        new UploadFile { FileName = "Test.zip" },
        new UploadFile { FileName = "Test.txt" },
        new UploadFile { FileName = "Test.dat" }
    };

    private List<UploadFile> Base64FormatFileList { get; } = new List<UploadFile>()
    {
        new UploadFile { FileName = "Test", PrevUrl = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACoAAAAkCAYAAAD/yagrAAAE60lEQVR4AWJwL/AB9GIWvI0jURz/L1NomZmZP8p9kWNmhnIbKPdomZmZGcJgO7QsWMbce55Yl11t47FbVdKTYpjOr/+H4y4Z/MpYeJVV8KVvokFRylq9osKfVtGQ/NHyPl2CbNVGwau2oOlmgUDJtDJGzxtvvIA3vRH+7IgeA0VtYhQBtKPlToFgC6RY58aggfwLNKhr0Zry2NnPrpIM2YGW2+UBG1IEmdGVJEVXE+hQXt8joKhLjdGVZHd7TSD9DHmT3J1dheroSF4fhnvUVQwbZx9UOnHSzQjkTN0tIG+8hFdbxet4PQOG4WqJwN1hFVb+xUBmCblxvxRkIE+Q+Yf0T31NSrp4/RV4FhHgoSTchQRZBK4D1+Fe2q2g8CYXU6buQyNDaiZKZhn0IYXHVwbkRQydHyawOAGGisa/o3DvI/jF3QKKiuAy+LKs5CvaXMLdeXb35wbkKTjmReDZHysCBosWJqN7r0jZ/VfgXtIlUNQnVpK7D4nNJSC5BHGi1d108Ppr8MxlF0cJqBSyFJaUfUnvHLyO4cttgaI+NhGB7HE0MaSUks/gU5vwe1gv5tfhmUmAhxME8jbIUtiEDus+fhmDJlgCRY0ylZRZhybKWtNinmYlH9NvL5puO3m9UHOkgzb3xuF5HCkDyhYiSwrYVfQPTpYChTc+E35tG9VJBjGFFNmtVlNMDnjzbx0EBpKq1eTeh2awbArBRuHadBWu6WVB4U/NITfuobYoCZl7QL//wDtr+nTmsgzQh2Kwgtz7QAZWFeGw/RI8s94Kigp1PvzZg2i9x11FDtKn/oZCoZdZxhaAXmG4/ohJKHudLE1Gyu66DOfs10BRR5CU3TKQIrtzj9BAkF991dsMslTZEClLsI+jFmANZYFAdAJl9QG03jWH9GrcFh9QTP4Of6GfJGSpsv1J2aoEKRuWCANNJNqOFAaPAW36rVRMCjVf6ZCtqYF2p6Asxg5mWK6tQamY9RCs8z1wF+FxTR5UqYT/3GC7oCkMHcjxKguq6cnl+Egf2whip3C9Yu56jk+GXXOtv1XIa8L1f3AFkHe9az83AmNanwV/bhfa5JKJ3n1slCUL8dk7CNfvVFMfySRThoxbK7fh18tTZXI2QeySLk88IRGsbHkqQj6wUJ72G5CloCXKZrdbLPgVMgVfQMq5m97bfQWOOeVbaK02nSA2ofn2y662UJE47horLZTe38oDjdxQUhmawocxa0OJ5hXjnZE4w1y0uc/iULKGfk+xNuZxI/BnjhFs+THPq4phmTsbeYPXRjFsJJ+NSMlnInHkxrwzGDjR3uBcG1/B/b/TwZkhubb6tP2oVfTAD2G4kw9vYiA2h+zy4GwYxd7S4qGOgd6oqVn6re1DTXiO4e4wPF+yQlF4ZCBfheSPIjJn+ciS/w93qjA6aZYqeQ5D3dTqvuE6GZNTkov5vqsYvLh7j8u1sWWkLIdBQR+q+XdtaKGhJCWBDpkwgSw9gRpKyoNKw6rjCLCDP4yVflSgWFwt3C0HSUo2BTFYX28fVAaWPpDx7wtwjOSSUszawnUT0KTudlfrFQwZ3WNf867CNZQhk/C8kIFUhJLt/O3Jzn62IYNwrUvA8zws4W61CGlDSfugXMz5pJgiSFYyXMYiRXdH4GmyqaR9UHLxzxG41KAwpZxRPN4kRf85Z5I4MvYfFUFGfemJG40AAAAASUVORK5CYII=" },
    };

    private Task OnFileChange(UploadFile file)
    {
        // 未真正保存文件
        // file.SaveToFile()
        Trace?.Log($"{file.File!.Name} {Localizer["Success"]}");
        return Task.FromResult("");
    }

    private async Task OnClickToUpload(UploadFile file)
    {
        // 示例代码,模拟 80% 几率保存成功
        var error = random.Next(1, 100) > 80;
        if (error)
        {
            file.Code = 1;
            file.Error = Localizer["Error"];
        }
        else
        {
            await SaveToFile(file);
        }
    }

    private CancellationTokenSource? UploadFolderToken { get; set; }
    private async Task OnUploadFolder(UploadFile file)
    {
        // 上传文件夹时会多次回调此方法
        await SaveToFile(file);
    }

    private CancellationTokenSource? ReadAvatarToken { get; set; }
    private async Task OnAvatarUpload(UploadFile file)
    {
        // 示例代码,使用 base64 格式
        if (file != null && file.File != null)
        {
            var format = file.File.ContentType;
            if (CheckValidAvatarFormat(format))
            {
                ReadAvatarToken ??= new CancellationTokenSource();
                if (ReadAvatarToken.IsCancellationRequested)
                {
                    ReadAvatarToken.Dispose();
                    ReadAvatarToken = new CancellationTokenSource();
                }

                await file.RequestBase64ImageFileAsync(format, 640, 480, MaxFileLength, ReadAvatarToken.Token);
            }
            else
            {
                file.Code = 1;
                file.Error = Localizer["FormatError"];
            }

            if (file.Code != 0)
            {
                await ToastService.Error(Localizer["AvatarMsg"], $"{file.Error} {format}");
            }
        }
    }

    private CancellationTokenSource? ReadToken { get; set; }

    private static long MaxFileLength => 200 * 1024 * 1024;

    private async Task OnCardUpload(UploadFile file)
    {
        if (file != null && file.File != null)
        {
            // 服务器端验证当文件大于 2MB 时提示文件太大信息
            if (file.Size > MaxFileLength)
            {
                await ToastService.Information(Localizer["FileMsg"], Localizer["FileError"]);
                file.Code = 1;
                file.Error = Localizer["FileError"];
            }
            else
            {
                await SaveToFile(file);
            }
        }
    }

    private async Task<bool> SaveToFile(UploadFile file)
    {
        // Server Side 使用
        // Web Assembly 模式下必须使用 webapi 方式去保存文件到服务器或者数据库中
        // 生成写入文件名称
        var ret = false;
        if (!string.IsNullOrEmpty(SiteOptions.CurrentValue.WebRootPath))
        {
            var uploaderFolder = Path.Combine(SiteOptions.CurrentValue.WebRootPath, $"images{Path.DirectorySeparatorChar}uploader");
            file.FileName = $"{Path.GetFileNameWithoutExtension(file.OriginFileName)}-{DateTimeOffset.Now:yyyyMMddHHmmss}{Path.GetExtension(file.OriginFileName)}";
            var fileName = Path.Combine(uploaderFolder, file.FileName);

            ReadToken ??= new CancellationTokenSource();
            ret = await file.SaveToFile(fileName, MaxFileLength, ReadToken.Token);

            if (ret)
            {
                // 保存成功
                file.PrevUrl = $"images/uploader/{file.FileName}";
            }
            else
            {
                var errorMessage = $"{Localizer["SaveFileError"]} {file.OriginFileName}";
                file.Code = 1;
                file.Error = errorMessage;
                await ToastService.Error(Localizer["UploadFile"], errorMessage);
            }
        }
        else
        {
            file.Code = 1;
            file.Error = Localizer["WasmError"];
            await ToastService.Information(Localizer["SaveFile"], Localizer["SaveFileMsg"]);
        }
        return ret;
    }

    private static bool CheckValidAvatarFormat(string format)
    {
        return "jpg;png;bmp;gif;jpeg".Split(';').Any(f => format.Contains(f, StringComparison.OrdinalIgnoreCase));
    }

    private Task<bool> OnFileDelete(UploadFile item)
    {
        Trace?.Log($"{item.OriginFileName} {Localizer["RemoveMsg"]}");
        return Task.FromResult(true);
    }

    private Person Foo { get; set; } = new Person();

    private static Task OnSubmit(EditContext context)
    {
        // 示例代码请根据业务情况自行更改
        // var fileName = Foo.Picture?.Name;
        return Task.CompletedTask;
    }

    [NotNull]
    private BlockLogger? AvatarTrace { get; set; }

    private Task OnAvatarValidSubmit(EditContext context)
    {
        AvatarTrace.Log(Foo.Picture?.Name ?? "");
        return Task.CompletedTask;
    }

    private async Task OnDownload(UploadFile item)
    {
        await ToastService.Success("文件下载", $"下载 {item.FileName} 成功");
    }

    private class Person
    {
        [Required]
        [StringLength(20, MinimumLength = 2)]
        public string Name { get; set; } = "Blazor";

        [Required]
        [FileValidation(Extensions = new string[] { ".png", ".jpg", ".jpeg" }, FileSize = 50 * 1024)]
        public IBrowserFile? Picture { get; set; }
    }

    private IEnumerable<AttributeItem> GetInputAttributes() => new AttributeItem[]
    {
        new AttributeItem() {
            Name = "ShowDeleteButton",
            Description = Localizer["ShowDeleteButton"],
            Type = "bool",
            ValueList = "true|false",
            DefaultValue = "false"
        },
        new AttributeItem() {
            Name = "IsDisabled",
            Description = Localizer["IsDisabled"],
            Type = "boolean",
            ValueList = "true / false",
            DefaultValue = "false"
        },
        new AttributeItem() {
            Name = "PlaceHolder",
            Description = Localizer["PlaceHolder"],
            Type = "string",
            ValueList = " — ",
            DefaultValue = " — "
        },
        new AttributeItem() {
            Name = "Accept",
            Description = Localizer["Accept"],
            Type = "string",
            ValueList = " — ",
            DefaultValue = " — "
        },
        new AttributeItem() {
            Name = "BrowserButtonClass",
            Description = Localizer["BrowserButtonClass"],
            Type = "string",
            ValueList = " — ",
            DefaultValue = "btn-primary"
        },
        new AttributeItem() {
            Name = "BrowserButtonIcon",
            Description = Localizer["BrowserButtonIcon"],
            Type = "string",
            ValueList = " — ",
            DefaultValue = "fa fa-folder-open-o"
        },
        new AttributeItem() {
            Name = "BrowserButtonText",
            Description = Localizer["BrowserButtonText"],
            Type = "string",
            ValueList = " — ",
            DefaultValue = ""
        },
        new AttributeItem() {
            Name = "DeleteButtonClass",
            Description = Localizer["DeleteButtonClass"],
            Type = "string",
            ValueList = " — ",
            DefaultValue = "btn-danger"
        },
        new AttributeItem() {
            Name = "DeleteButtonIcon",
            Description = Localizer["DeleteButtonIcon"],
            Type = "string",
            ValueList = " — ",
            DefaultValue = "fa fa-trash-o"
        },
        new AttributeItem() {
            Name = "DeleteButtonText",
            Description = Localizer["DeleteButtonText"],
            Type = "string",
            ValueList = " — ",
            DefaultValue = Localizer["DeleteButtonTextDefaultValue"]
        },
        new AttributeItem() {
            Name = "OnDelete",
            Description = Localizer["OnDelete"],
            Type = "Func<string, Task<bool>>",
            ValueList = " — ",
            DefaultValue = " — "
        },
        new AttributeItem() {
            Name = "OnChange",
            Description = Localizer["OnChange"],
            Type = "Func<UploadFile, Task>",
            ValueList = " — ",
            DefaultValue = " — "
        }
    };

    private IEnumerable<AttributeItem> GetButtonAttributes() => new AttributeItem[]
    {
        new AttributeItem() {
            Name = "IsDirectory",
            Description = Localizer["IsDirectory"],
            Type = "bool",
            ValueList = "true|false",
            DefaultValue = "false"
        },
        new AttributeItem() {
            Name = "IsMultiple",
            Description = Localizer["IsMultiple"],
            Type = "bool",
            ValueList = "true|false",
            DefaultValue = "false"
        },
        new AttributeItem() {
            Name = "IsSingle",
            Description = Localizer["IsSingle"],
            Type = "bool",
            ValueList = "true|false",
            DefaultValue = "false"
        },
        new AttributeItem() {
            Name = "ShowProgress",
            Description = Localizer["ShowProgress"],
            Type = "bool",
            ValueList = "true|false",
            DefaultValue = "false"
        },
        new AttributeItem() {
            Name = "Accept",
            Description = Localizer["Accept"],
            Type = "string",
            ValueList = " — ",
            DefaultValue = " — "
        },
        new AttributeItem() {
            Name = "BrowserButtonClass",
            Description = Localizer["BrowserButtonClass"],
            Type = "string",
            ValueList = " — ",
            DefaultValue = "btn-primary"
        },
        new AttributeItem() {
            Name = "BrowserButtonIcon",
            Description = Localizer["BrowserButtonIcon"],
            Type = "string",
            ValueList = " — ",
            DefaultValue = "fa fa-folder-open-o"
        },
        new AttributeItem() {
            Name = "BrowserButtonText",
            Description = Localizer["BrowserButtonText"],
            Type = "string",
            ValueList = " — ",
            DefaultValue = ""
        },
        new AttributeItem() {
            Name = "DefaultFileList",
            Description = Localizer["DefaultFileList"],
            Type = "List<UploadFile>",
            ValueList = " — ",
            DefaultValue = " — "
        },
        new AttributeItem() {
            Name = "OnGetFileFormat",
            Description = Localizer["OnGetFileFormat"],
            Type = "Func<string, string>",
            ValueList = " — ",
            DefaultValue = " — "
        },
        new AttributeItem() {
            Name = "OnDelete",
            Description = Localizer["OnDelete"],
            Type = "Func<string, Task<bool>>",
            ValueList = " — ",
            DefaultValue = " — "
        },
        new AttributeItem() {
            Name = "OnChange",
            Description = Localizer["OnChange"],
            Type = "Func<UploadFile, Task>",
            ValueList = " — ",
            DefaultValue = " — "
        },
        new AttributeItem() {
            Name = "OnDownload",
            Description = Localizer["OnDownload"],
            Type = "Func<UploadFile, Task>",
            ValueList = " — ",
            DefaultValue = " — "
        }
    };

    private IEnumerable<AttributeItem> GetAvatarAttributes() => new AttributeItem[]
    {
        new AttributeItem() {
            Name = "Width",
            Description = Localizer["Width"],
            Type = "int",
            ValueList = " — ",
            DefaultValue = "0"
        },
        new AttributeItem() {
            Name = "Height",
            Description = Localizer["Height"],
            Type = "int",
            ValueList = "—",
            DefaultValue = "0"
        },
        new AttributeItem() {
            Name = "IsCircle",
            Description = Localizer["IsCircle"],
            Type = "bool",
            ValueList = "true|false",
            DefaultValue = "false"
        },
        new AttributeItem() {
            Name = "IsSingle",
            Description = Localizer["IsSingle"],
            Type = "bool",
            ValueList = "true|false",
            DefaultValue = "false"
        },
        new AttributeItem() {
            Name = "ShowProgress",
            Description = Localizer["ShowProgress"],
            Type = "bool",
            ValueList = "true|false",
            DefaultValue = "false"
        },
        new AttributeItem() {
            Name = "Accept",
            Description = Localizer["Accept"],
            Type = "string",
            ValueList = " — ",
            DefaultValue = " — "
        },
        new AttributeItem() {
            Name = "DefaultFileList",
            Description = Localizer["DefaultFileList"],
            Type = "List<UploadFile>",
            ValueList = " — ",
            DefaultValue = " — "
        },
        new AttributeItem() {
            Name = "OnDelete",
            Description = Localizer["OnDelete"],
            Type = "Func<string, Task<bool>>",
            ValueList = " — ",
            DefaultValue = " — "
        },
        new AttributeItem() {
            Name = "OnChange",
            Description = Localizer["OnChange"],
            Type = "Func<UploadFile, Task>",
            ValueList = " — ",
            DefaultValue = " — "
        }
    };

    /// <summary>
    /// 
    /// </summary>
    public void Dispose()
    {
        UploadFolderToken?.Dispose();
        ReadAvatarToken?.Cancel();
        ReadToken?.Cancel();
        GC.SuppressFinalize(this);
    }
}

B 站相关视频链接

交流群

QQ Group:BootstrapAdmin & Blazor 795206915 675147445 Welcome to join the group discussion
Themes
Bootstrap
Motronic
Ant Design (完善中)
LayUI (完善中)
An error has occurred. This application may no longer respond until reloaded. Reload
Seems like the connection with the server has been lost. It can be due to poor or broken network. Please hang on while we're trying to reconnect...
Oh snap! Failed to reconnect with the server. This is typically caused by a longer network outage, or if the server has been taken down. You can try to reconnect, but if that does not work, you need to reload the page.
Oh man! The server rejected the attempt to reconnect. The only option now is to reload the page, but be prepared that it won't work, since this is typically caused by a failure on the server.
Bootstrap Blazor 组件库 更新到 6.6.0

首先感谢您对本套组件的关注,本目前本套组件已经拥有超过 120 个组件,本组件是基于 Bootstrap 风格的 Blazor 企业级组件库,提供如布局、导航、表单、数据、通知、图标、语音等几大类通用组件,每一个组件都经过静心设计,具有模块化、响应式和优秀的性能。从更多实际场景出发,满足多种场景的需求,极大的减少开发者时间成本,大大缩短开发周期,大幅提高开发效率,并提供了一套 通用权限管理系统 示例工程。Bootstrap Blazor 产品是由一支专业的全职技术团队进行维护,高效的响应速度,多元化的解决方案,长期提供支持,并提供企业级支持。目前已在多家知名国企内部使用,项目最高在线 1200 人稳定运行。右侧为国内人数最多的中文 Blazor QQ 社区二维码,欢迎扫描加群。

组件更新到 6.6.0 更新日志 [传送门] 如果组件给您带来了方便,请您帮忙给项目点亮 Star github gitee

QQGroup
QQ 795206915