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 相关文章推荐
在b/s开发中经常用到的javaScript技术
Aug 23 Javascript
关于图片验证码设计的思考
Jan 29 Javascript
JQuery的Alert消息框插件使用介绍
Oct 09 Javascript
快速查找数组中的某个元素并返回下标示例
Sep 03 Javascript
jQuery使用Selectator插件实现多选下拉列表过滤框(附源码下载)
Apr 08 Javascript
JS实现移动端整屏滑动的实例代码
Nov 10 Javascript
vue项目中应用ueditor自定义上传按钮功能
Apr 27 Javascript
vuejs2.0运用原生js实现简单拖拽元素功能
Aug 21 Javascript
JS中如何轻松遍历对象属性的方式总结
Aug 06 Javascript
React路由鉴权的实现方法
Sep 05 Javascript
jquery实现直播弹幕效果
Nov 28 jQuery
vue动画—通过钩子函数实现半场动画操作
Aug 09 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
PHP XML备份Mysql数据库
2009/05/27 PHP
PHP中使用php://input处理相同name值的表单数据
2015/02/03 PHP
Laravel 实现密码重置功能
2018/02/23 PHP
js实现的网站首页随机公告随机公告
2007/03/14 Javascript
JS JSON对象转为字符串的简单实现方法
2013/11/18 Javascript
Javascript中浮点数相乘的一个解决方法
2014/06/03 Javascript
jQuery实现简单网页遮罩层/弹出层效果兼容IE6、IE7
2014/06/16 Javascript
基于jquery的文字向上跑动类似跑马灯的效果
2014/09/22 Javascript
解决Jquery向页面append新元素之后事件的绑定问题
2015/03/16 Javascript
jquery显示loading图片直到网页加载完成的方法
2015/06/25 Javascript
JS建造者模式基本用法实例分析
2015/06/30 Javascript
JavaScript判断数组是否包含指定元素的方法
2015/07/01 Javascript
js实现跨域的几种方法汇总(图片ping、JSONP和CORS)
2015/10/25 Javascript
jsonp跨域请求实现示例
2017/03/13 Javascript
javascript Function函数理解与实战
2017/12/01 Javascript
详解vue-cli项目中怎么使用mock数据
2018/05/29 Javascript
vue-router重定向不刷新问题的解决
2018/06/25 Javascript
小程序异步问题之多个网络请求依次执行并依次收集请求结果
2019/05/05 Javascript
vue keep-alive列表页缓存 详情页返回上一页不刷新,定位到之前位置
2019/11/26 Javascript
js屏蔽F12审查元素,禁止修改页面代码等实现代码
2020/10/02 Javascript
[01:03:59]2018DOTA2亚洲邀请赛3月30日 小组赛B组VGJ.T VS Secret
2018/03/31 DOTA
python3爬取各类天气信息
2018/02/24 Python
解决tensorflow测试模型时NotFoundError错误的问题
2018/07/27 Python
python3连接kafka模块pykafka生产者简单封装代码
2019/12/23 Python
python打印异常信息的两种实现方式
2019/12/24 Python
如何基于python测量代码运行时间
2019/12/25 Python
什么是Python变量作用域
2020/06/03 Python
python 如何把docker-compose.yaml导入到数据库相关条目里
2021/01/15 Python
基于HTML5超酷摄像头(HTML5 webcam)拍照功能实现代码
2012/12/13 HTML / CSS
阿根廷网上配眼镜:SmartBuyGlasses阿根廷
2016/08/19 全球购物
应届生求职简历的自我评价怎么写
2013/10/23 职场文书
公司总经理工作职责管理办法
2014/02/28 职场文书
基层干部2014全国两会学习心得体会
2014/03/10 职场文书
运动员获奖感言
2014/08/15 职场文书
物业客服专员岗位职责
2015/04/07 职场文书
2016中秋节问候语
2015/11/11 职场文书