后端abp,前端vue导入excel,开始准备用直接用npoi,觉得要写太多的代码,就算从以前的复制粘贴也麻烦,所以偷懒直接用别人的轮子

Magicodes.IE。这样可以节省很多工作,根据实体生成excel模板、支持枚举、导入时自动验证数据是否合法(必填、类型等)


Excel模板

要导入首先要有录入数据的excel模板,以前都是把模板做好,放到服务器上,给一个下载链接给用户下载,这里可以直接用对象动态生成模板。

  1. //ExcelAppService.cs
  2. /// <summary>
  3. /// 生成excel模板
  4. /// </summary>
  5. /// <typeparam name="T">模板内容实体</typeparam>
  6. /// <param name="fileName">下载文件名称</param>
  7. /// <returns>输出文件流</returns>
  8. internal async Task<FileContentResult> GetTemplate<T>(string fileName = "模板") where T : class, new()
  9. {
  10. byte[] fileBytes = await importer.GenerateTemplateBytes<T>();
  11. return new FileContentResult(fileBytes, System.Net.Mime.MediaTypeNames.Application.Octet)
  12. {
  13. FileDownloadName = $"{fileName}.xlsx"
  14. };
  15. }

importer是在构造函数中注入的IImporter类型,如果你使用注入需要先在module的Initialize()方法中注册。

  1. //module.Initialize()方法
  2. IocManager.Register<Magicodes.ExporterAndImporter.Core.IImporter, Magicodes.ExporterAndImporter.Excel.ExcelImporter>(DependencyLifeStyle.Transient);

你也可以直接使用

  1. IImporter importer=new ExcelImporter()

生成模板就做完了,剩下的就是在需要下载的地方调用此方法,公开一个api接口就可以了

  1. /// <summary>
  2. /// 下载导入模板
  3. /// </summary>
  4. /// <returns></returns>
  5. public async Task<ActionResult> GetTemplate()
  6. {
  7. return await excelAppService.GetTemplate<XXXXImportExcelDto>();
  8. }

XXXXImportExcelDto是导入的实体类型,具体定义方式可以见https://github.com/xin-lai/Magicodes.IE

如果你用的abp官方提供的vue项目,使用的axios请求后端,也就是ajax请求,这个文件流是不会弹出保存文件框的,需要在axios请求后拦截文件流弹出下载框。找到src\lib\ajax.ts文件,修改ajax.interceptors.response方法,并添加一个downloadUrl方法

  1. ajax.interceptors.response.use((respon)=>{
  2. ++ //拦截文件下载请求
  3. ++ if (respon.headers && (respon.headers['content-type'] === 'application/octet-stream')) {
  4. ++ downloadUrl(respon.request.responseURL)
  5. ++ respon.data='';
  6. ++ respon.headers['content-type'] = 'text/json'
  7. ++ return respon;
  8. ++ }
  9. return respon
  10. },(error)=>{
  11. if(!!error.response&&!!error.response.data.error&&!!error.response.data.error.message&&error.response.data.error.details){
  12. vm.$Modal.error({title:error.response.data.error.message,content:error.response.data.error.details})
  13. }else if(!!error.response&&!!error.response.data.error&&!!error.response.data.error.message){
  14. vm.$Modal.error({title:window.abp.localization.localize("LoginFailed"),content:error.response.data.error.message})
  15. }else if(!error.response){
  16. vm.$Modal.error(window.abp.localization.localize('UnknownError'));
  17. }
  18. setTimeout(()=>{
  19. vm.$Message.destroy();
  20. },1000);
  21. return Promise.reject(error);
  22. })
  23. ++const downloadUrl = url => {
  24. ++ let iframe = document.createElement('iframe')
  25. ++ iframe.style.display = 'none'
  26. ++ iframe.src = url
  27. ++ iframe.onload = function () {
  28. ++ document.body.removeChild(iframe)
  29. ++ }
  30. ++ document.body.appendChild(iframe)
  31. ++}

导入excel

导入分为两步:上传excel文件和解析数据。由于没有找到一个一次能处理这两步的方法(因为需要指定解析后的类型,这是一个强类型参数),我采用的方式是:

  1. 加一个自定义组件,主要用于上传,提供一个上传完成事件,在上传完成后触发事件并传入后台excel文件的名称,
  2. 使用的地方绑定事件并把带着文件名请求后台,
  3. 后台再调用通用方法的解析数据

定义组件

  1. <template>
  2. <div>
  3. <Upload
  4. :action="uploadURL"
  5. :on-success="onSuccess"
  6. accept=".xls, .xlsx"
  7. :show-upload-list="false"
  8. >
  9. <Button icon="android-add" type="primary">{{btnTitle}}</Button>
  10. </Upload>
  11. </div>
  12. </template>
  13. <script lang="ts">
  14. import { Component, Vue, Inject, Prop, Watch } from "vue-property-decorator";
  15. import Util from "../../lib/util";
  16. import AbpBase from "../../lib/abpbase";
  17. import appconst from "../../lib/appconst";
  18. @Component
  19. export default class ImportExcel extends AbpBase {
  20. uploadURL =
  21. appconst.remoteServiceBaseUrl + "/api/services/app/Excel/UploadExcelFile";
  22. async onSuccess(response, file, fileList) {
  23. //上传完成触发事件uploadCompleted
  24. this.$emit("uploadCompleted", response.result);
  25. }
  26. /**按钮显示内容 */
  27. @Prop({ type: String, default: "" }) btnTitle: String;
  28. }
  29. </script>
  30. <style lang="less" scoped>
  31. </style>

后端接收文件方法

  1. //ExcelAppService.cs
  2. /// <summary>
  3. /// 接收上传文件方法
  4. /// </summary>
  5. /// <param name="file">文件内容</param>
  6. /// <returns>文件名称</returns>
  7. public async Task<string> UploadExcelFile(IFormFile file)
  8. {
  9. //FileDir是存储临时文件的目录,相对路径
  10. //private const string FileDir = "/File/ExcelTemp";
  11. string url = await WriteFile(file, FileDir);
  12. string fullpath = Path.GetFullPath($"{Environment.CurrentDirectory}" + url);
  13. return Path.GetFileName(url);
  14. }
  15. /// <summary>
  16. /// 写入文件
  17. /// </summary>
  18. /// <param name="avatar"></param>
  19. /// <param name="reDir"></param>
  20. /// <returns></returns>
  21. public async Task<string> WriteFile(IFormFile avatar, string reDir)
  22. {
  23. string reName = Guid.NewGuid() + Path.GetExtension(avatar.FileName);
  24. string dir = GetDirPath(reDir);
  25. string path = $"{dir}\\{reName}";
  26. Stream stream = avatar.OpenReadStream();
  27. using (FileStream fileStream = new FileStream(path, FileMode.Create))
  28. {
  29. await avatar.CopyToAsync(fileStream);
  30. }
  31. return $"{reDir}/{reName}";
  32. }
  33. public string GetDirPath(string reDir)
  34. {
  35. string dir = $"{Environment.CurrentDirectory}/{reDir}";
  36. if (!Directory.Exists(dir))
  37. {
  38. Directory.CreateDirectory(dir);
  39. }
  40. return Path.GetFullPath(dir);
  41. }

使用组件

  1. <template>
  2. <ImprotExcel @uploadCompleted="importExcel" :btnTitle="'导入excel'" ></ImprotExcel>
  3. </template>
  4. <script>
  5. async importExcel(fileName: string) {
  6. //请求后端api
  7. await this.$store.dispatch({
  8. type: "xxx/importExcel",
  9. data: { fileName: fileName, labId: this.labId }
  10. });
  11. (<any>this.$Message).success({ background: true, content: "导入成功" });
  12. }
  13. </script>

后端解析文件方法

  1. /// <summary>
  2. /// 导入
  3. /// </summary>
  4. /// <param name="input">导入excel参数</param>
  5. /// <returns></returns>
  6. [HttpPost]
  7. public async Task ImportExcel(XXXImprotExcelInput input)
  8. {
  9. var data = await excelAppService.GetData<XXXImportExcelDto>(input.FileName);
  10. if (!data.Any())
  11. {
  12. return;
  13. }
  14. //你的逻辑
  15. }
  16. //XXXImprotExcelInput.cs
  17. /// <summary>
  18. /// 导入excel
  19. /// </summary>
  20. public class XXXImprotExcelInput
  21. {
  22. /// <summary>
  23. /// 上传的excel文件名称
  24. /// </summary>
  25. public string FileName { get; set; }
  26. //你的其他参数
  27. }
  28. //ExcelAppService.cs
  29. /// <summary>
  30. /// 解析excel数据
  31. /// </summary>
  32. /// <typeparam name="T">要解析的数据类型</typeparam>
  33. /// <param name="fileName">excel文件名称,不含路径</param>
  34. /// <returns></returns>
  35. internal async Task<IEnumerable<T>> GetData<T>(string fileName) where T : class, new()
  36. {
  37. var fullpath = GetFullPath(fileName);
  38. var result = await importer.Import<T>(fullpath);
  39. if (result.HasError)
  40. {
  41. var errFile = Path.GetFileNameWithoutExtension(fileName) + "_" + Path.GetExtension(fileName);
  42. //如果excel文件内容不符合要求(格式错误、必填数据未填、数据类型错误),则弹出错误提示并给出下载链接
  43. throw new UserFriendlyException("导入错误", GetErrorExcelDownLoadUrl(errFile));
  44. }
  45. return result.Data;
  46. }
  47. /// <summary>
  48. /// 下载excel文件
  49. /// </summary>
  50. /// <param name="fileName"></param>
  51. /// <returns></returns>
  52. [HttpGet]
  53. public async Task<FileContentResult> DownLoadFile(string fileName)
  54. {
  55. var fullPath = GetFullPath(fileName);
  56. byte[] fileBytes = await File.ReadAllBytesAsync(fullPath);
  57. return new FileContentResult(fileBytes, System.Net.Mime.MediaTypeNames.Application.Octet)
  58. {
  59. FileDownloadName = fileName
  60. };
  61. }
  62. /// <summary>
  63. /// 获取文件全路径
  64. /// </summary>
  65. /// <param name="fileName"></param>
  66. /// <returns></returns>
  67. private string GetFullPath(string fileName)
  68. {
  69. fileName = Path.GetFileName(fileName);
  70. var fullpath = Path.GetFullPath(Environment.CurrentDirectory.EnsureEndsWith('/') + FileDir.EnsureEndsWith('/') + fileName);
  71. return fullpath;
  72. }
  73. /// <summary>
  74. /// 获取excel下载链接
  75. /// </summary>
  76. /// <param name="fileName"></param>
  77. /// <returns></returns>
  78. private string GetErrorExcelDownLoadUrl(string fileName)
  79. {
  80. return $"请按照excel文件内的错误提示修改后再次导入,<a href='{GetHost()}/api/services/app/Excel/DownLoadFile?fileName={fileName}' target='_blank'>点击下载excel</a>"
  81. ;
  82. }
  83. /// <summary>
  84. /// 获取当前域名地址
  85. /// </summary>
  86. /// <returns></returns>
  87. private string GetHost()
  88. {
  89. var req = httpContextAccessor.HttpContext.Request;
  90. return $"{req.Scheme}://{req.Host}";
  91. }

参考资料

abp_vue导入导出excel的更多相关文章

  1. ASP.NET Core 导入导出Excel xlsx 文件

    ASP.NET Core 使用EPPlus.Core导入导出Excel xlsx 文件,EPPlus.Core支持Excel 2007/2010 xlsx文件导入导出,可以运行在Windows, Li ...

  2. thinkphp导入导出excel表单数据

    在PHP项目经常要导入导出Excel表单. 先去下载PHPExcel类库文件,放到相应位置. 我在thinkphp框架中的位置为ThinkPHP/Library/Org/Util/ 导入 在页面上传e ...

  3. 导入导出Excel工具类ExcelUtil

    前言 前段时间做的分布式集成平台项目中,许多模块都用到了导入导出Excel的功能,于是决定封装一个ExcelUtil类,专门用来处理Excel的导入和导出 本项目的持久化层用的是JPA(底层用hibe ...

  4. php中导入导出excel的原理

    在php中我们要经常导入导出excel文件,方便后台管理.那么php导入和导出excel的原理到底是什么呢?excel分为两大版本excel2007(后缀.xlsx).excel2003(后缀.xls ...

  5. NPOI导入导出EXCEL通用类,供参考,可直接使用在WinForm项目中

    以下是NPOI导入导出EXCEL通用类,是在别人的代码上进行优化的,兼容xls与xlsx文件格式,供参考,可直接使用在WinForm项目中,由于XSSFWorkbook类型的Write方法限制,Wri ...

  6. .NET导入导出Excel

    若是开发后台系统,ASP.NET MVC中总是涉及了很多导入导出Excel的问题,有的时候处理起来比较烦 如果能使用以下代码解决,就完美了 public class ReportModel { [Ex ...

  7. Java利用POI导入导出Excel中的数据

         首先谈一下今天发生的一件开心的事,本着一颗android的心我被分配到了PB组,身在曹营心在汉啊!好吧,今天要记录和分享的是Java利用POI导入导出Excel中的数据.下面POI包的下载地 ...

  8. .Net MVC 导入导出Excel总结(三种导出Excel方法,一种导入Excel方法) 通过MVC控制器导出导入Excel文件(可用于java SSH架构)

    .Net MVC  导入导出Excel总结(三种导出Excel方法,一种导入Excel方法) [原文地址] 通过MVC控制器导出导入Excel文件(可用于java SSH架构)   public cl ...

  9. jxl导入/导出excel

    1.jxl导入/导出excel案例,黏贴即可运行 package junit.test; import java.io.File; import java.io.IOException; import ...

随机推荐

  1. mysql优化过程中遇见的坑(mysql优化问题特别注意)

    不要听信你看到的关于优化的“绝对真理”,包括本文所讨论的内容,而应该是在实际的业务场景下通过测试来验证你关于执行计划以及响应时间的假设. 单条查询最后添加 LIMIT 1,停止全表扫描. 对于char ...

  2. scala 中的匹配模式

    unapply 仅作匹配,不作其它输出.返回 Boolean 值 object UpperCase { def unapply(s: String): Boolean = s.toUpperCase ...

  3. wordpress模板加载顺序汇总

    我们要创建一个新的wordpress模板需要先了解有哪些页面模板,这些页面模板的文件是什么?它们是怎么工作的?下面ytkah汇总了一些常用的wordpress模板结构方便大家查找 首页 首先WordP ...

  4. 字符串翻转(C++)

    1.字符串原地翻转,"abc"->"cba": int str_reverse(string &str,int first,int last) { ...

  5. python - django 控制台输出 sql 语句

    只需要在 settings.py 文件中加入以下配置即可. LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers ...

  6. Java字符串之间拼接时,如果有null值,则会直接拼接上null

    package com.fgy.demo; public class demo06 { public static void main(String[] args) { String str1 = & ...

  7. CSS行内块元素(内联元素)

    一.典型代表 input img 二.特点: 在一行上显示 可以设置宽高 <style type="text/css"> img{ width: 300px; /* 顶 ...

  8. Xamarin.Forms 开发热加载利器 HotReload 推荐

    https://github.com/AndreiMisiukevich/HotReload

  9. gerrit配置跳过审核直接push到gitlab

    项目中有存放项目相关的文档,这些项目需要配置跳过审核再提交的操作.现在需要给某些组配置不审核直接提交的权限 方法: 使用管理员账号,到 projects -> access 页面下配置 refe ...

  10. I Count Two Three(打表+排序+二分查找)

    I Count Two Three 二分查找用lower_bound 这道题用cin,cout会超时... AC代码: /* */ # include <iostream> # inclu ...