当前位置: 首页 > news >正文

spring + angular 实现导出excel

需求描述

要求批量导出数据,以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的时候,很多时候,我们并不能一下子就找到我们想要的东西,但是并不是说这在做无用功,因为我们往往会在一些类似的文章中找到灵感。

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


相关参考:
https://my.oschina.net/u/3644...
https://blog.csdn.net/LUNG108...

相关文章:

  • 专访Pony.ai 楼天城:自动驾驶已经走过了“从0到1”,“规模”是行业的分水岭| 自动驾驶这十年 ...
  • Git for Windows 2.21.0 发布,Win 下的 Git 客户端
  • 聊聊flink的BlobWriter
  • PDF旋转使用的转换器有哪些
  • luoguP4647 [IOI2007] sails 船帆
  • 乐视云计算被列入失信名单;三星华为达成和解;Python3 采用率超 84%丨Q新闻
  • django -- 修改admin 密码问题
  • Java 最常见的 200+ 面试题:面试必备
  • lvm管理卷之缩减卷大小
  • 使用WebShellKiller检查服务器后门文件
  • quasar-framework cnodejs社区
  • 前端面试必懂的 - http 网络知识
  • 3月7日云栖精选夜读 | RSA 2019安全大会:企业资产管理成行业新风向标,云上安全占绝对优势 ...
  • Grafana v6.0.1 发布,系统指标监控与分析平台
  • 兼容:获取浏览器滚动位置
  • [ JavaScript ] 数据结构与算法 —— 链表
  • 【翻译】Mashape是如何管理15000个API和微服务的(三)
  • 2017-08-04 前端日报
  • angular学习第一篇-----环境搭建
  • C# 免费离线人脸识别 2.0 Demo
  • eclipse的离线汉化
  • MySQL几个简单SQL的优化
  • Python - 闭包Closure
  • Python代码面试必读 - Data Structures and Algorithms in Python
  • React系列之 Redux 架构模式
  • Redux系列x:源码分析
  • 给新手的新浪微博 SDK 集成教程【一】
  • 经典排序算法及其 Java 实现
  • 普通函数和构造函数的区别
  • 如何抓住下一波零售风口?看RPA玩转零售自动化
  • Oracle Portal 11g Diagnostics using Remote Diagnostic Agent (RDA) [ID 1059805.
  • 从如何停掉 Promise 链说起
  • 蚂蚁金服CTO程立:真正的技术革命才刚刚开始
  • ​ ​Redis(五)主从复制:主从模式介绍、配置、拓扑(一主一从结构、一主多从结构、树形主从结构)、原理(复制过程、​​​​​​​数据同步psync)、总结
  • ​一文看懂数据清洗:缺失值、异常值和重复值的处理
  • !!java web学习笔记(一到五)
  • # 睡眠3秒_床上这样睡觉的人,睡眠质量多半不好
  • #我与Java虚拟机的故事#连载16:打开Java世界大门的钥匙
  • (C语言)输入一个序列,判断是否为奇偶交叉数
  • (Redis使用系列) Springboot 使用Redis+Session实现Session共享 ,简单的单点登录 五
  • (vue)页面文件上传获取:action地址
  • (附源码)springboot家庭财务分析系统 毕业设计641323
  • (附源码)ssm基于微信小程序的疫苗管理系统 毕业设计 092354
  • (简单有案例)前端实现主题切换、动态换肤的两种简单方式
  • (论文阅读笔记)Network planning with deep reinforcement learning
  • (免费领源码)Python#MySQL图书馆管理系统071718-计算机毕业设计项目选题推荐
  • (篇九)MySQL常用内置函数
  • (十)T检验-第一部分
  • (译) 函数式 JS #1:简介
  • (原創) 如何動態建立二維陣列(多維陣列)? (.NET) (C#)
  • .Net Attribute详解(上)-Attribute本质以及一个简单示例
  • .net core 6 redis操作类
  • .NET 将混合了多个不同平台(Windows Mac Linux)的文件 目录的路径格式化成同一个平台下的路径
  • // an array of int
  • [ 第一章] JavaScript 简史