后端abp,前端vue导入excel,开始准备用直接用npoi,觉得要写太多的代码,就算从以前的复制粘贴也麻烦,所以偷懒直接用别人的轮子
Magicodes.IE。这样可以节省很多工作,根据实体生成excel模板、支持枚举、导入时自动验证数据是否合法(必填、类型等)
Excel模板
要导入首先要有录入数据的excel模板,以前都是把模板做好,放到服务器上,给一个下载链接给用户下载,这里可以直接用对象动态生成模板。
//ExcelAppService.cs
///
/// 生成excel模板
///
/// 模板内容实体
/// 下载文件名称
/// 输出文件流
internal async Task GetTemplate(string fileName = "模板") where T : class, new()
{
byte[] fileBytes = await importer.GenerateTemplateBytes();
return new FileContentResult(fileBytes, System.Net.Mime.MediaTypeNames.Application.Octet)
{
FileDownloadName = $"{fileName}.xlsx"
};
}
importer是在构造函数中注入的IImporter类型,如果你使用注入需要先在module的Initialize()方法中注册。
//module.Initialize()方法
IocManager.Register(DependencyLifeStyle.Transient);
你也可以直接使用
IImporter importer=new ExcelImporter()
生成模板就做完了,剩下的就是在需要下载的地方调用此方法,公开一个api接口就可以了
///
/// 下载导入模板
///
///
public async Task GetTemplate()
{
return await excelAppService.GetTemplate();
}
XXXXImportExcelDto是导入的实体类型,具体定义方式可以见https://github.com/xin-lai/Magicodes.IE
如果你用的abp官方提供的vue项目,使用的axios请求后端,也就是ajax请求,这个文件流是不会弹出保存文件框的,需要在axios请求后拦截文件流弹出下载框。找到src\lib\ajax.ts文件,修改ajax.interceptors.response方法,并添加一个downloadUrl方法
ajax.interceptors.response.use((respon)=>{
++ //拦截文件下载请求
++ if (respon.headers && (respon.headers['content-type'] === 'application/octet-stream')) {
++ downloadUrl(respon.request.responseURL)
++ respon.data='';
++ respon.headers['content-type'] = 'text/json'
++ return respon;
++ }
return respon
},(error)=>{
if(!!error.response&&!!error.response.data.error&&!!error.response.data.error.message&&error.response.data.error.details){
vm.$Modal.error({title:error.response.data.error.message,content:error.response.data.error.details})
}else if(!!error.response&&!!error.response.data.error&&!!error.response.data.error.message){
vm.$Modal.error({title:window.abp.localization.localize("LoginFailed"),content:error.response.data.error.message})
}else if(!error.response){
vm.$Modal.error(window.abp.localization.localize('UnknownError'));
}
setTimeout(()=>{
vm.$Message.destroy();
},1000);
return Promise.reject(error);
})
++const downloadUrl = url => {
++ let iframe = document.createElement('iframe')
++ iframe.style.display = 'none'
++ iframe.src = url
++ iframe.onload = function () {
++ document.body.removeChild(iframe)
++ }
++ document.body.appendChild(iframe)
++}
导入excel
导入分为两步:上传excel文件和解析数据。由于没有找到一个一次能处理这两步的方法(因为需要指定解析后的类型,这是一个强类型参数),我采用的方式是:
- 加一个自定义组件,主要用于上传,提供一个上传完成事件,在上传完成后触发事件并传入后台excel文件的名称,
- 使用的地方绑定事件并把带着文件名请求后台,
- 后台再调用通用方法的解析数据
定义组件
后端接收文件方法
//ExcelAppService.cs
///
/// 接收上传文件方法
///
/// 文件内容
/// 文件名称
public async Task UploadExcelFile(IFormFile file)
{
//FileDir是存储临时文件的目录,相对路径
//private const string FileDir = "/File/ExcelTemp";
string url = await WriteFile(file, FileDir);
string fullpath = Path.GetFullPath($"{Environment.CurrentDirectory}" + url);
return Path.GetFileName(url);
}
///
/// 写入文件
///
///
///
///
public async Task WriteFile(IFormFile avatar, string reDir)
{
string reName = Guid.NewGuid() + Path.GetExtension(avatar.FileName);
string dir = GetDirPath(reDir);
string path = $"{dir}\\{reName}";
Stream stream = avatar.OpenReadStream();
using (FileStream fileStream = new FileStream(path, FileMode.Create))
{
await avatar.CopyToAsync(fileStream);
}
return $"{reDir}/{reName}";
}
public string GetDirPath(string reDir)
{
string dir = $"{Environment.CurrentDirectory}/{reDir}";
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
}
return Path.GetFullPath(dir);
}
使用组件
后端解析文件方法
///
/// 导入
///
/// 导入excel参数
///
[HttpPost]
public async Task ImportExcel(XXXImprotExcelInput input)
{
var data = await excelAppService.GetData(input.FileName);
if (!data.Any())
{
return;
}
//你的逻辑
}
//XXXImprotExcelInput.cs
///
/// 导入excel
///
public class XXXImprotExcelInput
{
///
/// 上传的excel文件名称
///
public string FileName { get; set; }
//你的其他参数
}
//ExcelAppService.cs
///
/// 解析excel数据
///
/// 要解析的数据类型
/// excel文件名称,不含路径
///
internal async Task> GetData(string fileName) where T : class, new()
{
var fullpath = GetFullPath(fileName);
var result = await importer.Import(fullpath);
if (result.HasError)
{
var errFile = Path.GetFileNameWithoutExtension(fileName) + "_" + Path.GetExtension(fileName);
//如果excel文件内容不符合要求(格式错误、必填数据未填、数据类型错误),则弹出错误提示并给出下载链接
throw new UserFriendlyException("导入错误", GetErrorExcelDownLoadUrl(errFile));
}
return result.Data;
}
///
/// 下载excel文件
///
///
///
[HttpGet]
public async Task DownLoadFile(string fileName)
{
var fullPath = GetFullPath(fileName);
byte[] fileBytes = await File.ReadAllBytesAsync(fullPath);
return new FileContentResult(fileBytes, System.Net.Mime.MediaTypeNames.Application.Octet)
{
FileDownloadName = fileName
};
}
///
/// 获取文件全路径
///
///
///
private string GetFullPath(string fileName)
{
fileName = Path.GetFileName(fileName);
var fullpath = Path.GetFullPath(Environment.CurrentDirectory.EnsureEndsWith('/') + FileDir.EnsureEndsWith('/') + fileName);
return fullpath;
}
///
/// 获取excel下载链接
///
///
///
private string GetErrorExcelDownLoadUrl(string fileName)
{
return $"请按照excel文件内的错误提示修改后再次导入,点击下载excel"
;
}
///
/// 获取当前域名地址
///
///
private string GetHost()
{
var req = httpContextAccessor.HttpContext.Request;
return $"{req.Scheme}://{req.Host}";
}
参考资料
- Magicodes.IE