spring+angular实现导出excel的实现代码


Posted in Javascript onFebruary 27, 2019

需求描述

要求批量导出数据,以excel的格式。

选择方式

前台 + 后台

之前在别的项目中也遇到过导出的问题,解决方式是直接在前台导出将表格导出。

这次没有选择前台导出的方式,是由于需要导出所有的数据,所以考虑直接在后台获取所有的数据,然后就直接导出,最后前台触发导出API。

后台实现

导出使用的是POI,在上一篇文章中,我已做了基本的介绍,这里就不做介绍配置了,参照:POI实现将导入Excel文件

创建表格

首先先建立一张表,这里要建立.xlsx格式的表格,使用XSSFWorkbook:

Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("new sheet");

接着创建表格的行和单元格:

Row row = sheet.createRow(0);
row.createCell(0);

然后设置表头:

row.createCell(0).setCellValue("学号");
row.createCell(1).setCellValue("姓名");
row.createCell(2).setCellValue("手机号码");

最后获取所有的数据,对应的填写到单元格中:

int i = 1;
for (Student student : studentList) {
  row = sheet.createRow(i);
  row.createCell(0).setCellValue(student.getStudentNumber());
  row.createCell(1).setCellValue(student.getName());
  row.createCell(2).setCellValue(student.getPhoneNumber());
  i++;
}

输出

这部分是纠结比较久的,反复试了很多次。

一开始是直接以文件输出流的形式输出的:

FileOutputStream output = new FileOutputStream("test.xlsx");
workbook.write(output);

这样可以正确生成文件,但是问题是,它会生成在项目的根目录下。

而我们想要的效果是,下载在本地自己的文件夹中。

要解决这个问题,需要添加相应信息,返回给浏览器:

OutputStream fos = response.getOutputStream();
response.reset();
String fileName = "test";
fileName = URLEncoder.encode(fileName, "utf8");
response.setHeader("Content-disposition", "attachment;filename="+ fileName+".xlsx");

response.setCharacterEncoding("UTF-8");
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
workbook.write(fos);
fos.close();

后台完成代码:

public void batchExport(HttpServletResponse response) {
  logger.debug("创建工作表");
  Workbook workbook = new XSSFWorkbook();
  Sheet sheet = workbook.createSheet("new sheet");

  logger.debug("获取所有学生");
  List<Student> studentList = (List<Student>) studentRepository.findAll();

  logger.debug("建立表头");
  Row row = sheet.createRow(0);
  row.createCell(0).setCellValue("学号");
  row.createCell(1).setCellValue("姓名");
  row.createCell(2).setCellValue("手机号码");

  logger.debug("将学生信息写入对应单元格");
  int i = 1;
  for (Student student : studentList) {
    row = sheet.createRow(i);
    row.createCell(0).setCellValue(student.getStudentNumber());
    row.createCell(1).setCellValue(student.getName());
    row.createCell(2).setCellValue(student.getPhoneNumber()); 
    i++;
  }

  OutputStream fos;
  try {
    fos = response.getOutputStream();
    response.reset();
    String fileName = "test";
    fileName = URLEncoder.encode(fileName, "utf8");
    response.setHeader("Content-disposition", "attachment;filename="+ fileName+".xlsx");

    response.setCharacterEncoding("UTF-8");
    response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");// 设置contentType为excel格式
    workbook.write(fos);
    fos.close();

  } catch (Exception e) {
      e.printStackTrace();
  }
}

前台实现

在前台调用的时候,也经历了多次失败,google了很多篇文章,各种各样的写法都有,自己也是试了试,前台后台都对照做了很多尝试,但基本都是有问题的。这里我值给出我最后选择配套后台的方法。

// 后台导出路由
const exportUrl = '/api/student/batchExport';

// 创建a标签,并点击
let a = document.createElement('a');
document.body.appendChild(a);
a.setAttribute('style', 'display:none');
a.setAttribute('href', exportUrl);
a.click();
URL.revokeObjectURL(exportUrl);

最后的实现还是一种比较简单的方法,创建了一个a标签,然后隐式点击。

注意到这里我没有使用http请求,主要是他并不能触发浏览器的下载,在发起请求后,并没有正确的生成文件,具体是什么还不清楚。后面弄明白后我会再更新这篇文章。

升级

上面的形式,在导出所有的数据的时候是没有问题的,但是如果我想带一些参数呢?

另外,我们的项目是建立在nginx同源的基础上,一旦出现跨域问题,前台向后台请求,浏览器是不会默认携带Cookie的,每次请求都将会被看作是一个新的请求。

所以上面的解决办法有所限制。

那么,还可以怎么写呢?

file-saver

这里我将借助FileSaver来帮助我在前台生成excel文件。

this.http.get('student/batchExport', { responseType: 'blob'})
  .subscribe(data => {
    let blob = new Blob([data], {type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8'});
    saveAs(blob, 'test.xlsx');
  });

用httpClient发起get请求,声明:响应类型为blob。

blob是一个用来存储二进制文件的对象。

然后创建一个blob对象,类型为excel格式。

最后,利用file-saver中的saveAs函数,将blob对象生成文件名为'test.xlsx'的excel文件。

调整后台

这里后台大部分和前面的是一样的,但是明眼人会发现,前台使用后面的方法后,下面的代码就多余了:

String fileName = "test";
fileName = URLEncoder.encode(fileName, "utf8");
response.setHeader("Content-disposition", "attachment;filename="+ fileName+".xlsx");

response.setCharacterEncoding("UTF-8");
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");

是的,我们将这一部分交由前台负责,所以后台对应的这部分就可以删除了,只使用response获取输出流就可以了:

OutputStream fos = response.getOutputStream();
workbook.write(fos);
fos.close();

好了,使用这种方法,我们就可以在发起http请求的时候,添加我们想要的参数了。

总结

我们在google的时候,很多时候,我们并不能一下子就找到我们想要的东西,但是并不是说这在做无用功,因为我们往往会在一些类似的文章中找到灵感。

所以,当我们没有直接找到我们想要的结果的时候,不妨大胆的做一些尝试,因为我们会在一次又一次失败的尝试中,慢慢的了解问题的原理到底是怎么回事。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
javascript 复杂的嵌套环境中输出单引号和双引号
May 26 Javascript
JavaScript 解析Json字符串的性能比较分析代码
Dec 16 Javascript
node.js中的fs.openSync方法使用说明
Dec 17 Javascript
正则表达式优化JSON字符串的技巧
Dec 24 Javascript
理解javascript中DOM事件
Dec 25 Javascript
JS 实现可停顿的垂直滚动实例代码
Nov 23 Javascript
史上最全JavaScript常用的简写技巧(推荐)
Aug 17 Javascript
详解ES6 系列之异步处理实战
Oct 26 Javascript
vue+vant-UI框架实现购物车的复选框全选和反选功能
Nov 05 Javascript
js 计算月/周的第一天和最后一天代码
Feb 01 Javascript
如何基于javascript实现贪吃蛇游戏
Feb 09 Javascript
基于angular实现树形二级表格
Oct 16 Javascript
react native 原生模块桥接的简单说明小结
Feb 26 #Javascript
JavaScript实现获取两个排序数组的中位数算法示例
Feb 26 #Javascript
小程序hover-class点击态效果实现
Feb 26 #Javascript
JS实现计算小于非负数n的素数的数量算法示例
Feb 26 #Javascript
vue使用Font Awesome的方法步骤
Feb 26 #Javascript
JS实现的杨辉三角【帕斯卡三角形】算法示例
Feb 26 #Javascript
create-react-app使用antd按需加载的样式无效问题的解决
Feb 26 #Javascript
You might like
PHP动态规划解决0-1背包问题实例分析
2015/03/23 PHP
一个无限级XML绑定跨框架菜单(For IE)
2007/01/27 Javascript
javascript 跳转代码集合
2009/12/03 Javascript
Jquery实现网页跳转或用命令打开指定网页的解决方法
2013/07/09 Javascript
javascript写的一个模拟阅读小说的程序
2014/04/04 Javascript
javascript与有限状态机详解
2014/05/08 Javascript
JS从数组中随机取出几个数组元素的方法
2016/08/02 Javascript
BooStrap对导航条的改造实践小结
2016/09/21 Javascript
vue.js初学入门教程(2)
2016/11/07 Javascript
jQuery插件echarts实现的去掉X轴、Y轴和网格线效果示例【附demo源码下载】
2017/03/04 Javascript
AngulaJS路由 ui-router 传参实例
2017/04/28 Javascript
将 vue 生成的 js 上传到七牛的实例
2017/07/28 Javascript
通俗解释JavaScript正则表达式快速记忆
2017/08/23 Javascript
JS实现的全排列组合算法示例
2017/10/09 Javascript
ES6 javascript的异步操作实例详解
2017/10/30 Javascript
浏览器调试动态js脚本的方法(图解)
2018/01/19 Javascript
15分钟深入了解JS继承分类、原理与用法
2019/01/19 Javascript
ES6 Object方法扩展的应用实例分析
2019/06/25 Javascript
微信小程序 扭蛋抽奖机css3动画实现详解
2019/07/19 Javascript
[01:25]DOTA2自定义游戏灵园鬼域等你踏足
2015/10/30 DOTA
Django中反向生成models.py的实例讲解
2018/05/30 Python
python从list列表中选出一个数和其对应的坐标方法
2019/07/20 Python
基于Python新建用户并产生随机密码过程解析
2019/10/08 Python
Python简单实现区域生长方式
2020/01/16 Python
如何基于pythonnet调用halcon脚本
2020/01/20 Python
Python进阶之迭代器与迭代器切片教程
2020/01/29 Python
Python用requests库爬取返回为空的解决办法
2021/02/21 Python
突袭HTML5之Javascript API扩展5—其他扩展(应用缓存/服务端消息/桌面通知)
2013/01/31 HTML / CSS
夏洛特和乔治婴儿和儿童时装精品店:Charlotte and George
2018/06/06 全球购物
美国购买新书和二手书网站:Better World Books
2018/10/31 全球购物
平安工地建设方案
2014/05/06 职场文书
2014年中秋寄语
2014/08/11 职场文书
工程承包协议书范本
2014/09/29 职场文书
药品销售员2015年终工作总结
2015/10/22 职场文书
写作技巧:如何撰写一份优秀的营销策划书
2019/08/13 职场文书
Python多线程实用方法以及共享变量资源竞争问题
2022/04/12 Python