1.本文主要內容為給Blazor工程構建通用導入導出服務類
基礎工程構建取自 [BootstrapBlazor實戰 10分鍾編寫數據庫維護] 項目,使用到的orm為freesql,導入導出庫為Magicodes.IE,還有封裝了Table組件的內存數據服務LazyHeroDataService的Densen.FreeSql.Extensions.BootstrapBlazor庫.
主要實現功能(Table組件):
- [編輯][添加][刪除]數據
- [導出]現在表格數據
- [清空]數據
- [選擇文件]上傳並導入
- [清空]數據后再選擇[導入]
- [自由編輯]類似Excel模式編輯表格
最終運行效果

2.使用 nuget.org 進行必須的組件安裝
dotnet add b03sqlite package Magicodes.IE.Core
dotnet add b03sqlite package Magicodes.IE.Excel
dotnet add b03sqlite package Magicodes.IE.Html
dotnet add b03sqlite package Magicodes.IE.Pdf
dotnet add b03sqlite package Magicodes.IE.Word
dotnet add b03sqlite package Densen.FreeSql.Extensions.BootstrapBlazor
Densen.FreeSql.Extensions.BootstrapBlazor 庫封裝了Table組件的內存數據服務LazyHeroDataService,因篇幅關系不展開介紹,需要了解的朋友請自行查看源碼 https://github.com/densen2014/Densen.Extensions/blob/master/Blazor/Services/LazyHeroDataService.cs
3.項目添加 Service 文件夾,添加 ImportExportsService.cs 文件
using Magicodes.ExporterAndImporter.Core;
using Magicodes.ExporterAndImporter.Excel;
using Magicodes.ExporterAndImporter.Html;
using Magicodes.ExporterAndImporter.Pdf;
using Magicodes.ExporterAndImporter.Word;
namespace Blazor100.Service
{
/// <summary>
/// 通用導入導出服務類
/// </summary>
public class ImportExportsService
{
public enum ExportType
{
Excel,
Pdf,
Word,
Html
}
public async Task<string> ExportToExcel<T>(string filePath, List<T>? items = null, ExportType exportType = ExportType.Excel) where T : class, new()
{
switch (exportType)
{
case ExportType.Pdf:
var exporterPdf = new PdfExporter();
items = items ?? new List<T>();
var resultPdf = await exporterPdf.ExportListByTemplate(filePath + ".pdf", items);
return resultPdf.FileName;
case ExportType.Word:
var exporterWord = new WordExporter();
items = items ?? new List<T>();
var resultWord = await exporterWord.ExportListByTemplate(filePath + ".docx", items);
return resultWord.FileName;
case ExportType.Html:
var exporterHtml = new HtmlExporter();
items = items ?? new List<T>();
var resultHtml = await exporterHtml.ExportListByTemplate(filePath + ".html", items);
return resultHtml.FileName;
default:
IExporter exporter = new ExcelExporter();
items = items ?? new List<T>();
var result = await exporter.Export(filePath + ".xlsx", items);
return result.FileName;
}
}
public async Task<(IEnumerable<T>? items,string error)> ImportFormExcel<T>(string filePath) where T : class, new()
{
IExcelImporter Importer = new ExcelImporter();
var import = await Importer.Import<T>(filePath);
if (import.Data == null )
{
return (null, import.Exception.Message);
}
return (import.Data!.ToList(),"");
}
}
}
4.添加增加命名空間引用到 _Imports.razor 文件中
@using Blazor100.Service
@using AME.Services
5.添加ImportExportsService服務到 Program.cs 文件中
using Blazor100.Service;
builder.Services.AddTransient<ImportExportsService>();
添加封裝了Table組件的內存數據服務LazyHeroDataService的DensenExtensions()
builder.Services.AddBootstrapBlazor(); 改為
builder.Services.AddDensenExtensions();
6.數據實體類
添加導入導出特性到 Data/WeatherForecast.cs 類文件中
完整文件
using BootstrapBlazor.Components;
using FreeSql.DataAnnotations;
using Magicodes.ExporterAndImporter.Excel;
using OfficeOpenXml.Table;
using System.ComponentModel;
namespace b03sqlite.Data;
[ExcelImporter(IsLabelingError = true)]
[ExcelExporter(Name = "導入商品中間表", TableStyle = TableStyles.Light10, AutoFitAllColumn = true)]
[AutoGenerateClass(Searchable = true, Filterable = true, Sortable = true)]
public class WeatherForecast
{
[Column(IsIdentity = true)]
[DisplayName("序號")]
public int ID { get; set; }
[DisplayName("日期")]
public DateTime Date { get; set; }
public int TemperatureC { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
public string? Summary { get; set; }
}
6.頁面文件 Pages/ImpExp.razor
@page "/impexp"
@using b03sqlite.Data
<PageTitle>導入導出</PageTitle>
<InputFile OnChange="OnChange" style="max-width:400px" class="form-control" />
<br/>
<Table @ref="list1"
TItem="WeatherForecast"
IsPagination="true"
IsStriped="true"
IsBordered="true"
AutoGenerateColumns="true"
ShowSearch="true"
ShowToolbar="true"
ShowExtendButtons="true"
DataService="LazyHeroDataService"
OnSaveAsync="LazyHeroDataService!.SaveAsync"
OnDeleteAsync="LazyHeroDataService.DeleteAsync"
DoubleClickToEdit="@DoubleClickToEdit"
IsExcel="@IsExcel"
ScrollingDialogContent="true"
EditDialogIsDraggable="true"
EditDialogSize="Size.ExtraLarge"
EditDialogShowMaximizeButton="true">
<TableToolbarTemplate>
<TableToolbarButton TItem="WeatherForecast" Color="Color.Primary" Text="自由編輯" OnClick="@IsExcelToggle" />
<TableToolbarButton TItem="WeatherForecast" Color="Color.Warning" Text="隨機數據" IsAsync OnClick="@GetDatasAsync" />
<TableToolbarButton TItem="WeatherForecast" Color="Color.Secondary" Text="導入" IsAsync OnClick="@ImportExcel" />
<TableToolbarButton TItem="WeatherForecast" Color="Color.Info" Text="導出" IsAsync OnClickCallback="ExportAsync" />
<TableToolbarButton TItem="WeatherForecast" Color="Color.Danger" Text="清空" IsAsync OnClick="EmptyAll" />
<TableToolbarButton TItem="WeatherForecast" Color="Color.Success" Text="模板" IsAsync OnClick="Export模板Async" />
</TableToolbarTemplate>
</Table>
7.代碼
@code{
[Inject] Microsoft.AspNetCore.Hosting.IWebHostEnvironment? HostEnvironment { get; set; }
[Inject] NavigationManager? navigationManager { get; set; }
[Inject] ImportExportsService? importExportsService { get; set; }
[Inject] ToastService? toastService { get; set; }
[Inject] WeatherForecastService? ForecastService { get; set; }
[Inject] LazyHeroDataService<WeatherForecast>? LazyHeroDataService { get; set; }
Table<WeatherForecast>? list1 { get; set; }
public bool IsExcel { get; set; }
public bool DoubleClickToEdit { get; set; } = true;
protected string UploadPath = "";
protected string? uploadstatus;
long maxFileSize = 1024 * 1024 * 15;
string? tempfilename;
//protected override async Task OnInitializedAsync()
//{
//LazyHeroDataService!.Items = (await ForecastService!.GetForecastAsync(DateTime.Now)).ToList();
//}
protected async Task GetDatasAsync()
{
LazyHeroDataService!.Items = (await ForecastService!.GetForecastAsync(DateTime.Now)).ToList();
await list1!.QueryAsync();
}
protected override void OnAfterRender(bool firstRender)
{
if (firstRender)
{
UploadPath = Path.Combine(HostEnvironment!.WebRootPath, "Upload");
if (!Directory.Exists(UploadPath)) Directory.CreateDirectory(UploadPath);
}
}
private Task IsExcelToggle()
{
IsExcel = !IsExcel;
DoubleClickToEdit = !IsExcel;
StateHasChanged();
return Task.CompletedTask;
}
public async Task<bool> Export模板Async()
{
await 模板下載();
return true;
}
private async Task 模板下載(List<WeatherForecast>? items = null)
{
var ufilename = LazyHeroDataService!.Items == null ? "模板" : "新數據";
var sFileName = ufilename + ".xlsx";
var filename = Path.Combine(UploadPath, ufilename);
await importExportsService!.ExportToExcel(filename, items);
toastService?.Success("提示", ufilename + "已生成");
//* 流化到前端處理也可以,不一定要用文件形式下載.
navigationManager?.NavigateTo($"Upload/{sFileName}", true);
//下載后清除文件
_ = Task.Run(() =>
{
Thread.Sleep(5000);
System.IO.File.Delete(filename);
});
}
public async Task<bool> ExportAsync(IEnumerable<WeatherForecast> Items)
{
await 模板下載(LazyHeroDataService!.Items);
return true;
}
public async Task<bool> EmptyAll()
{
LazyHeroDataService!.Items = new List<WeatherForecast>();
await toastService!.Show(new ToastOption()
{
Category = ToastCategory.Success,
Title = "提示",
Content = "已清空數據",
});
await list1!.QueryAsync();
return true;
}
private async Task ImportExcel()
{
if (string.IsNullOrEmpty(tempfilename))
{
toastService?.Error("提示", "請正確選擇文件上傳");
return;
}
var option = new ToastOption()
{
Category = ToastCategory.Information,
Title = "提示",
Content = "導入文件中,請稍等片刻...",
IsAutoHide = false
};
// 彈出 Toast
await toastService!.Show(option);
await Task.Delay(100);
// 開啟后台進程進行數據處理
var isSuccess= await MockImportExcel();
// 關閉 option 相關聯的彈窗
await option.Close();
// 彈窗告知下載完畢
await toastService.Show(new ToastOption()
{
Category = isSuccess? ToastCategory.Success : ToastCategory.Error,
Title = "提示",
Content = isSuccess ? "操作成功,請檢查數據":"出現錯誤,請重試導入或者上傳",
IsAutoHide = false
});
await list1!.QueryAsync();
}
private async Task<bool> MockImportExcel()
{
var items_temp = await importExportsService!.ImportFormExcel<WeatherForecast>(tempfilename!);
if (items_temp.items == null)
{
toastService?.Error("提示", "文件導入失敗: "+ items_temp.error);
return false;
}
//items = SmartCombine(items_temp, items).ToList(); 新數據和老數據合並處理,略100字
LazyHeroDataService!.Items = items_temp!.items.ToList();
return true;
}
protected async Task OnChange(InputFileChangeEventArgs e)
{
if (e.File == null) return;
tempfilename = Path.Combine(UploadPath, e.File.Name);
await using FileStream fs = new(tempfilename, FileMode.Create);
using var stream = e.File.OpenReadStream(maxFileSize);
await stream.CopyToAsync(fs);
//正式工程此處是回調,簡化版必須InvokeAsync一下,自由發揮
_ = Task.Run(async () => await InvokeAsync(async () => await ImportExcel()));
}
}
8.試試Index.razor添加Tab組件看看效果
<Tab>
<TabItem Text="導入導出">
<ImpExp />
</TabItem>
<TabItem Text="數據維護">
... 原始頁面內容
</TabItem>
</Tab>
9.運行逐一測試功能
- 點擊[隨機數據] 生成數據
- [編輯][添加][刪除]數據
- [導出]現在表格數據
- [清空]數據
- [選擇文件]上傳並導入
- [清空]數據后再選擇[導入]
- [自由編輯]類似Excel模式編輯表格
- [導出PDF] 自行擴展 importExportsService.ExportToExcel(filename, items,ExportType.Pdf);
- [導出Word] 自行擴展 importExportsService.ExportToExcel(filename, items,ExportType.Word);
- [導出Html] 自行擴展 importExportsService.ExportToExcel(filename, items,ExportType.Html);



項目源碼
關聯項目
FreeSql QQ群:4336577(已滿)、8578575(已滿)、52508226(在線)
BA & Blazor QQ群:795206915、675147445
知識共享許可協議
本作品采用 知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議 進行許可。歡迎轉載、使用、重新發布,但務必保留文章署名AlexChow(包含鏈接: https://github.com/densen2014 ),不得用於商業目的,基於本文修改后的作品務必以相同的許可發布。如有任何疑問,請與我聯系 。
