Vue+axios+WebApi+NPOI导出Excel文件实例方法


Posted in Javascript onJune 05, 2019

一、前言

项目中前端采用的Element UI 框架, 远程数据请求,使用的是axios,后端接口框架采用的asp.net webapi,数据导出成Excel采用NPOI组件。其业务场景,主要是列表页(如会员信息,订单信息等)表格数据导出,如表格数据进行了条件筛选,则需要将条件传至后端api,筛选数据后,导出成Excel。

思考过前端导出的3种方案:

1.使用location.href 打开接口地址.缺点: 不能传token至后端api, 无法保证接口的安全性校验,并且接口只能是get方式请求.

2.采用axios请求接口,先在筛选后的数据服务端生成文件并保存,然后返回远程文件地址,再采用 location.href打开文件地址进行下载. 缺点: 实现复杂,并且每次导出会在服务端生成文件,但是又没有合适的时机再次触发删除文件,会在服务端形成垃圾数据。优点:每次导出都可以有记录。

3. 采用axios请求接口,服务端api返回文件流,前端接收到文件流后,采用blob对象存储,并创建成url, 使用a标签下载. 优点:前端可传token参数校验接口安全性,并支持get或post两种方式。

因其应用场景是导出Excel文件之前,必须筛选数据,并需要对接口安全进行校验,所以第3种方案为最佳选择。在百度之后,发现目前使用最多的也是第3种方案。

二、Vue + axios 前端处理

1.axios 需在response拦截器里进行相应的处理(这里不再介绍axios的使用, 关于axios的用法,具体请查看Axios中文说明 ,我们在项目中对axios进行了统一的拦截定义). 需特别注意: response.headers['content-disposition'],默认是获取不到的,需要对服务端webapi进行配置,请查看第三点中webapi CORS配置

// respone拦截器
service.interceptors.response.use(
 response => {
 // blob类型为文件下载对象,不论是什么请求方式,直接返回文件流数据
 if (response.config.responseType === 'blob') {
  const fileName = decodeURI(
  response.headers['content-disposition'].split('filename=')[1]
  )
// 返回文件流内容,以及获取文件名, response.headers['content-disposition']的获取, 默认是获取不到的,需要对服务端webapi进行配置
  return Promise.resolve({ data: response.data, fileName: fileName })
 }
 // 依据后端逻辑实际情况,需要弹窗展示友好错误
 },
 error => {
 let resp = error.response
 if (resp.data) {
  console.log('err:' + decodeURIComponent(resp.data)) // for debug
 }
 // TODO: 需要依据后端实际情况判断
 return Promise.reject(error)
 }
)

2. 点击导出按钮,请求api. 需要注意的是接口请求配置的响应类型responseType:'blob' (也可以是配置arrayBuffer) ; 然IE9及以下浏览器不支持createObjectURL. 需要特别处理下IE浏览器将blob转换成文件。

exportExcel () {
  let params = {}
  let p = this.getQueryParams() // 获取相应的参数
  if (p) params = Object({}, params, p)
  axios
  .get('接口地址', {
   params: params,
   responseType: 'blob'
  })
  .then(res => {
   var blob = new Blob([res.data], {
   type: 'application/vnd.ms-excel;charset=utf-8'
   })
   // 针对于IE浏览器的处理, 因部分IE浏览器不支持createObjectURL
   if (window.navigator && window.navigator.msSaveOrOpenBlob) {
   window.navigator.msSaveOrOpenBlob(blob, res.fileName)
   } else {
   var downloadElement = document.createElement('a')
   var href = window.URL.createObjectURL(blob) // 创建下载的链接
   downloadElement.href = href
   downloadElement.download = res.fileName // 下载后文件名
   document.body.appendChild(downloadElement)
   downloadElement.click() // 点击下载
   document.body.removeChild(downloadElement) // 下载完成移除元素
   window.URL.revokeObjectURL(href) // 释放掉blob对象
   }
  })
 }

三、WebApi + NPOI 后端处理

1. 需要通过接口参数,查询数据

为了保持与分页组件查询接口一直的参数,采用了get请求方式,方便前端传参。webapi接口必须返回IHttpActionResult 类型

[HttpGet]
  public IHttpActionResult ExportData([FromUri]Pagination pagination, [FromUri] OrderReqDto dto)
  {
   //取出数据源
   DataTable dt = this.Service.GetMemberPageList(pagination, dto.ConvertToFilter());
   if (dt.Rows.Count > 65535)
   {
    throw new Exception("最大导出行数为65535行,请按条件筛选数据!");
   }
   foreach (DataRow row in dt.Rows)
   {
    var isRealName = row["IsRealName"].ToBool();
    row["IsRealName"] = isRealName ? "是" : "否";
   }
   var model = new ExportModel();
   model.Data = JsonConvert.SerializeObject(dt);
   model.FileName = "会员信息";
   model.Title = model.FileName;
   model.LstCol = new List<ExportDataColumn>();
   model.LstCol.Add(new ExportDataColumn() { prop = "FullName", label = "会员名称" });
   model.LstCol.Add(new ExportDataColumn() { prop = "RealName", label = "真实姓名" });
   model.LstCol.Add(new ExportDataColumn() { prop = "GradeName", label = "会员等级" });
   model.LstCol.Add(new ExportDataColumn() { prop = "Telphone", label = "电话" });
   model.LstCol.Add(new ExportDataColumn() { prop = "AreaName", label = "区域" });
   model.LstCol.Add(new ExportDataColumn() { prop = "GridName", label = "网格" });
   model.LstCol.Add(new ExportDataColumn() { prop = "Address", label = "门牌号" });
   model.LstCol.Add(new ExportDataColumn() { prop = "RegTime", label = "注册时间" });
   model.LstCol.Add(new ExportDataColumn() { prop = "Description", label = "备注" });
   return ExportDataByFore(model);
  }

2.关键导出函数 ExportDataByFore的实现

[HttpGet]
  public IHttpActionResult ExportDataByFore(ExportModel dto)
  {
   var dt = JsonConvert.DeserializeObject<DataTable>(dto.Data);
   var fileName = dto.FileName + DateTime.Now.ToString("yyMMddHHmmssfff") + ".xls";
   //设置导出格式
   ExcelConfig excelconfig = new ExcelConfig();
   excelconfig.Title = dto.Title;
   excelconfig.TitleFont = "微软雅黑";
   excelconfig.TitlePoint = 25;
   excelconfig.FileName = fileName;
   excelconfig.IsAllSizeColumn = true;
   //每一列的设置,没有设置的列信息,系统将按datatable中的列名导出
   excelconfig.ColumnEntity = new List<ColumnEntity>();
   //表头
   foreach (var col in dto.LstCol)
   {
    excelconfig.ColumnEntity.Add(new ColumnEntity() { Column = col.prop, ExcelColumn = col.label });
   }
   //调用导出方法
   var stream = ExcelHelper.ExportMemoryStream(dt, excelconfig); // 通过NPOI形成将数据绘制成Excel文件并形成内存流
   var browser = String.Empty;
   if (HttpContext.Current.Request.UserAgent != null)
   {
    browser = HttpContext.Current.Request.UserAgent.ToUpper();
   }
   HttpResponseMessage httpResponseMessage = new HttpResponseMessage(HttpStatusCode.OK);
   httpResponseMessage.Content = new StreamContent(stream);
   httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); // 返回类型必须为文件流 application/octet-stream
   httpResponseMessage.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") // 设置头部其他内容特性, 文件名
   {
    FileName =
     browser.Contains("FIREFOX")
      ? fileName
      : HttpUtility.UrlEncode(fileName)
   };
   return ResponseMessage(httpResponseMessage);
  }

3. web api 的CORS配置

采用web api 构建后端接口服务的同学,都知道,接口要解决跨域问题,都需要进行api的 CORS配置, 这里主要是针对于前端axios的响应response header中获取不到 content-disposition属性,进行以下配置

Vue+axios+WebApi+NPOI导出Excel文件实例方法

四、总结

以上就是我在实现axios导出Excel文件功能时,遇到的一些问题,以及关键点进行总结。因为项目涉及到前后端其他业务以及公司架构的一些核心源码。要抽离一个完整的demo,比较耗时,所以没有完整demo展示. 但我已把NPOI相关的操作函数源码,整理放至github上。https://github.com/yinboxie/BlogExampleDemo

Javascript 相关文章推荐
ajax请求get与post的区别总结
Nov 04 Javascript
JavaScrip常见的一些算法总结
Dec 28 Javascript
全面了解JS中的匿名函数
Jun 29 Javascript
js导出excel文件的简洁方法(推荐)
Nov 02 Javascript
JavaScript中boolean类型之三种情景实例代码
Nov 21 Javascript
如何制作幻灯片(代码分享)
Jan 06 Javascript
原生JS中slice()方法和splice()区别
Mar 06 Javascript
jQuery 导航自动跟随滚动的实现代码
May 30 jQuery
JS限制输入框输入的实现代码
Jul 02 Javascript
在react中使用vuex的示例代码
Jul 30 Javascript
解决antd 表单设置默认值initialValue后验证失效的问题
Nov 02 Javascript
CocosCreator入门教程之网络通信
Apr 16 Javascript
js实现随机8位验证码
Jul 24 #Javascript
Vue中全局变量的定义和使用
Jun 05 #Javascript
详解express使用vue-router的history踩坑
Jun 05 #Javascript
laravel-admin 与 vue 结合使用实例代码详解
Jun 04 #Javascript
用webpack4开发小程序的实现方法
Jun 04 #Javascript
JS实现的对象去重功能示例
Jun 04 #Javascript
JS数组中对象去重操作示例
Jun 04 #Javascript
You might like
消息持续发送的完整例子
2006/10/09 PHP
phplock(php进程锁) v1.0 beta1
2009/11/24 PHP
Laravel框架自定义公共函数的引入操作示例
2019/04/16 PHP
使用Javascript和DOM Interfaces来处理HTML
2006/10/09 Javascript
jQuery 打造动态下滑菜单实现说明
2010/04/15 Javascript
jquery自动切换tabs选项卡的具体实现
2013/12/24 Javascript
js数值和和字符串进行转换时可以对不同进制进行操作
2014/03/05 Javascript
JS实现点击按钮后框架内载入不同网页的方法
2015/05/05 Javascript
avalon js实现仿google plus图片多张拖动排序附源码下载
2015/09/24 Javascript
模仿password输入框的实现代码
2016/06/07 Javascript
用Angular实时获取本地Localstorage数据,实现一个模拟后台数据登入的效果
2016/11/09 Javascript
通过AngularJS实现图片上传及缩略图展示示例
2017/01/03 Javascript
NodeJs中express框架的send()方法简介
2017/06/20 NodeJs
详解在HTTPS 项目中使用百度地图 API
2019/04/26 Javascript
JS实现利用闭包判断Dom元素和滚动条的方向示例
2019/08/26 Javascript
layui输入框只允许输入中文且判断长度的例子
2019/09/18 Javascript
JS通过识别id、value值对checkbox设置选中状态
2020/02/19 Javascript
vue pages 多入口项目 + chainWebpack 全局引用缩写说明
2020/09/21 Javascript
剖析Python的Tornado框架中session支持的实现代码
2015/08/21 Python
Python中getattr函数和hasattr函数作用详解
2016/06/14 Python
Tensorflow训练模型越来越慢的2种解决方案
2020/02/07 Python
基于Python绘制个人足迹地图
2020/06/01 Python
TensorFlow-gpu和opencv安装详细教程
2020/06/30 Python
CSS3实现菜单悬停效果
2020/11/17 HTML / CSS
Rossignol金鸡美国官网:始于1907年法国百年雪具品牌
2019/03/06 全球购物
请写出 float x 与"零值"比较的 if 语句
2016/01/04 面试题
现在输入n个数字,以逗号,分开;然后可选择升或者降序排序;按提交键就在另一页面显示按什么排序,结果为,提供reset
2012/11/09 面试题
高中体育教学反思
2014/01/24 职场文书
社区学雷锋活动策划方案
2014/01/30 职场文书
社区母亲节活动方案
2014/03/05 职场文书
竞聘上岗演讲稿
2014/05/16 职场文书
乡党政领导班子群众路线教育实践活动个人对照检查材料
2014/09/20 职场文书
党员作风建设自查报告
2014/10/23 职场文书
期末复习计划
2015/01/19 职场文书
邓小平文选读书笔记
2015/06/29 职场文书
动漫APP软件排行榜前十名,半次元上榜,第一款由腾讯公司推出
2022/03/18 杂记