作者: Memory(星哥) Wechat:/QQ: 574373426
整理不易,感谢支持,欢迎 收藏 转发 分享
专注IT职业教育多年,学编程找星哥
前后端分离
后端采用 SpringBoot框架,整合 web , Mybtais-Plus , Apache POI , lombok
前端采用 Vue, ElementUI 做一个简易的页面发起请求(只有一个按钮)
数据库采用 Mysql
开发工具 IDEA2020.3.4
前端页面采用Vue+ElementUI
为了方便测试,没有构建Vue项目,直接引入对应的js文件
采用cdn在线引入的方式,也可以下载到本地引入
发起请求的方式: 采用 window.open(url)
作用: 发起请求,在新窗口打开,后续我们点击导出后,会在新窗口下载excel文件
也可以使用axios或者其他ajax发起请求,但需要注意要设置返回值类型
这里为了方便 直接使用 window.open(url) 效果是一样的
效果如下: 只有一个按钮
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> </head> <body> <!-- Vue开发环境版本,包含了有帮助的命令行警告 --> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <!-- Element 引入样式 --> <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css"> <!-- Element引入组件库 --> <script src="https://unpkg.com/element-ui/lib/index.js"></script> <div id="app"> <el-button type="primary" @click="exportTest">导出</el-button> </div> <script> var vue = new Vue({ el: "#app", methods: { exportTest(){ // 这里可以传入一些查询参数,我在这里传入了标题内容 // 将标题内容作为导出的Excel文件名,此处的标题内容后期可动态改变 var url = 'http://localhost:8080/reportExport/prodTest?excelTitle=用户管理' window.open(url) } } }) </script> </body> </html>
使用阿里云脚手架创建SpringBoot项目: https://start.aliyun.com
勾选Web依赖
创建项目后手动在pom.xml中添加也行 , 图个方便 , 就顺手勾一下
web,POI,jdbc,Mybatis-Plus,lombok
<dependencies> <!--web依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> <!-- POI EXCEL 文件读写 --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>4.1.2</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-excelant</artifactId> <version>4.1.2</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>4.1.2</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml-schemas</artifactId> <version>4.1.2</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-scratchpad</artifactId> <version>4.1.2</version> </dependency> <!--jdbc依赖包--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!--spring整合mybatis-plus --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.3</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus</artifactId> <version>3.4.3</version> </dependency> <!--添加lombok的包--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies>
User表存储 用户ID 用户名 密码 用户状态信息
后期将此数据导出到Excel表格中
数据库文件
#创建数据库 CREATE database poidata DEFAULT CHARACTER SET utf8; #使用数据库 use poidata; #创建表格 create table user( id int primary key auto_increment, name varchar(10), pwd varchar(10), status int ) charset utf8; #插入数据 insert into user(id,name,pwd,status) value (null,'张三','123',1); insert into user(id,name,pwd,status) value (null,'学编程找星哥','574373426',1); insert into user(id,name,pwd,status) value (null,'李四','123',0);
server: port: 8080 #管理数据源 spring: datasource: #高版本驱动使用 driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/poidata?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true #设定用户名和密码 username: root password: root
在 com.xg.pojo 中创建实体类User
实体类属性要与数据库字段名保持一致
@Data public class User { private Integer id; private String name; private String pwd; private Integer status; }
在com.xg.mapper下创建ReportMapper
并继承BaseMapper,必须范型User
Mapper 继承该接口后,无需编写 mapper.xml 文件,即可获得CRUD功能
package com.xg.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.xg.pojo.User; import org.apache.ibatis.annotations.Mapper; @Mapper public interface ReportMapper extends BaseMapper<User> { }
在controller方法中可直接注入HttpServletResponse对象
package com.xg.controller; import com.xg.service.ReportService; import org.apache.tools.ant.util.DateUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.GetMapping; import javax.servlet.http.HttpServletResponse; import java.io.BufferedOutputStream; import java.io.IOException; import java.io.OutputStream; import java.net.URLEncoder; import java.util.Date; @Controller @CrossOrigin //允许跨域请求 public class RequestController { @Autowired ReportService reportService; @GetMapping("/reportExport/prodTest") public Object prodTest(HttpServletResponse response, String excelTitle) throws IOException { // 创建导出文件名称 当前日期+前台传递过来的标题名(excelTitle) String fileName = DateUtils.format(new Date(),"yyyyMMddHHmmss") +"-"+excelTitle+".xls"; // 设置返回的消息头和返回值类型 并设置编码 不设置编码文件名为中文的话 不会显示 // 当设置成如下返回值时,浏览器才会执行下载文件动作 response.setHeader("Content-disposition", "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8")); response.setContentType("APPLICATION/OCTET-STREAM;charset=UTF-8"); // 创建输出流,调用service中exportTest方法,参数:输出流 标题名 reportService.exportTest(response.getOutputStream(), excelTitle); return null; } }
在com.xg.service包下创建 ReportService 接口
package com.xg.service; import java.io.IOException; import java.io.OutputStream; public interface ReportService { void exportTest(OutputStream out, String excelTitle) throws IOException; }
在com.xg.service包下创建 ReportServiceImpl 实现类,重写exportTest方法
exportTest方法接收Controller传递过来的输出流以及标题名
package com.xg.service; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.xg.mapper.ReportMapper; import com.xg.pojo.User; import com.xg.util.ExportExcel; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @Service public class ReportServiceImpl implements ReportService{ @Autowired ReportMapper reportMappe; @Override public void exportTest(OutputStream out, String excelTitle) throws IOException { // 定义列标 就是一个Excel的标题而已 下面有图介绍 String[] rowsName = new String[]{"用户id", "用户名", "密码", "是否禁用"}; // 创建导出数据集合 后续会将dataList中的数据写到Excel List<Object[]> dataList = new ArrayList<Object[]>(); // 从数据库查询用户列表 QueryWrapper queryWrapper = new QueryWrapper(); List<User> userList = reportMappe.selectList(queryWrapper); User user = null; // 将用户列表信息封装到一个Object数组 // 我这里封装Object数组 是为了方便后续代码复用,不会将对象类型写死 // 当然也可以在下一层使用反射来做,太麻烦了 还是这样转一下吧 for (int i=0;i<userList.size();i++){ //将数据库查到的每条数据 循环封装到Object[] user=userList.get(i); Object[] objs = new Object[]{user.getId(),user.getName(),user.getPwd(),user.getStatus()}; //将转换好的数据 存入dataList dataList.add(objs); } // 创建ExportExcel工具类对象 通过构造方法赋值 ExportExcel ex = new ExportExcel(excelTitle, rowsName, dataList); try { // 调用生成Excel的方法,将数据通过输出流写出 ex.export(out); } catch (Exception e) { e.printStackTrace(); } out.flush(); out.close(); } }
这个工具类里封装的就是Apache POI 给我们提供的对象,用来创建一个Excel表格
一定要仔细看以下图文,看懂你才能了解业务代码,话不多说,线上成果图,结合结果进行理解
首先在生成Excel前,我们需要理解一下Excel文件的构成。
在POI中式这样理解的 :
一个Excel对应一个workbook对象
一个workbook是由若干个sheet组成
一个sheet有多个row 行
一个row有多个列cell 列/单元格
对于生成Excel,POI提供了如下几个基本对象
HSSFWorkbook : excel的文档对象
HSSFSheet : excel的一个sheet
HSSFRow : excel的行
HSSFCell : excel的子单元格
ExportExcel工具类
HSSFCellStyle()方法是设置 表格标题样式
HSSFCellStyle()方法是设置 表格数据样式
当然样式不设置也行,我们导出Excel后手动在Excel中设置也行,千万不要认为代码一大堆看起来就烦,其实真正核心的代码没多少
标题(title) 就是上图中蓝色背景的用户管理,当然不设标题也行,随便喽
列标就是上图 用户id,用户名,密码,是否禁用,一般表格都有列标题吧
package com.xg.util; import org.apache.poi.hssf.usermodel.*; import org.apache.poi.ss.usermodel.*; import org.apache.poi.ss.util.CellRangeAddress; import java.io.OutputStream; import java.util.ArrayList; import java.util.List; public class ExportExcel { // 导出表的标题 private String title; // 导出表的列名 private String[] rowName; // 导出表的数据 private List<Object[]> dataList = new ArrayList<Object[]>(); // 构造函数,传入要导出的数据 public ExportExcel(String title, String[] rowName, List<Object[]> dataList) { this.dataList = dataList; this.rowName = rowName; this.title = title; } // 导出数据 public void export(OutputStream out) throws Exception { try { //1.创建一个workbook,一个workbook 对应一个Excel文件 HSSFWorkbook workbook = new HSSFWorkbook(); //2.创建 sheet excel中多个sheet组成一个excel文件 至少有一个sheet HSSFSheet sheet = workbook.createSheet(title); //3.在sheet中添加表头第0行 HSSFRow rowm = sheet.createRow(0); //4.创建单元格 HSSFCell cellTitle = rowm.createCell(0); //5.定义标题样式 和 数据样式 HSSFCellStyle columnTopStyle = this.getColumnTopStyle(workbook); HSSFCellStyle style = this.getStyle(workbook); sheet.addMergedRegion(new CellRangeAddress(0, 1, 0, (rowName.length - 1))); cellTitle.setCellStyle(columnTopStyle); //6.给单元格设置值 cellTitle.setCellValue(title); //7.获取列标长度 int columnNum = rowName.length; // 创建列 相当于一行 2代表第三行 因为上面的总标题占了两行为 0 1 HSSFRow rowRowName = sheet.createRow(2); //8.将列标题设置到单元格中 for (int n = 0; n < columnNum; n++) { HSSFCell cellRowName = rowRowName.createCell(n); cellRowName.setCellType(CellType.STRING); HSSFRichTextString text = new HSSFRichTextString(rowName[n]); cellRowName.setCellValue(text); cellRowName.setCellStyle(style); } //9.将数据设置到单元格中 for (int i = 0; i < dataList.size(); i++) { Object[] obj = dataList.get(i); HSSFRow row = sheet.createRow(i + 3); for (int j = 0; j < obj.length; j++) { HSSFCell cell = null; cell = row.createCell(j, CellType.STRING); if (!"".equals(obj[j]) && obj[j] != null) { cell.setCellValue(obj[j].toString()); } else { cell.setCellValue(" "); } cell.setCellStyle(style); } } if (workbook != null) { try { //10.将整个表格写出 workbook.write(out); } catch (Exception e) { e.printStackTrace(); } } } catch (Exception e) { } } /** * 表格标题样式 */ public HSSFCellStyle getColumnTopStyle(HSSFWorkbook workbook) { // 设置字体 HSSFFont font = workbook.createFont(); // 设置字体大小 font.setFontHeightInPoints((short) 11); // 设置字体颜色 font.setColor(IndexedColors.WHITE.getIndex()); // 字体加粗 font.setBold(true); // 设置字体名字 font.setFontName("Courier New"); // 设置样式 HSSFCellStyle style = workbook.createCellStyle(); // 设置标题背景色 style.setFillForegroundColor(IndexedColors.DARK_TEAL.getIndex()); // 设置背景颜色填充样式 style.setFillPattern(FillPatternType.SOLID_FOREGROUND); // 设置低边框 style.setBorderBottom(BorderStyle.THIN); // 设置低边框颜色 style.setBottomBorderColor(IndexedColors.ROYAL_BLUE.getIndex()); // 设置右边框 style.setBorderRight(BorderStyle.THIN); // 设置顶边框 style.setTopBorderColor(IndexedColors.ROYAL_BLUE.getIndex()); // 设置顶边框颜色 style.setTopBorderColor(IndexedColors.ROYAL_BLUE.getIndex()); // 在样式中应用设置的字体 style.setFont(font); // 设置自动换行 style.setWrapText(false); // 设置水平对齐的样式为居中对齐; style.setAlignment(HorizontalAlignment.CENTER); style.setVerticalAlignment(VerticalAlignment.CENTER); return style; } /** * 表格数据样式 */ public HSSFCellStyle getStyle(HSSFWorkbook workbook) { // 设置字体 HSSFFont font = workbook.createFont(); // 设置字体大小 font.setFontHeightInPoints((short) 10); // 设置字体名字 font.setFontName("Courier New"); // 设置样式; HSSFCellStyle style = workbook.createCellStyle(); // 设置底边框; style.setBorderBottom(BorderStyle.THIN); // 设置底边框颜色; style.setBottomBorderColor(IndexedColors.ROYAL_BLUE.getIndex()); // 设置左边框; style.setBorderLeft(BorderStyle.THIN); // 设置左边框颜色; style.setLeftBorderColor(IndexedColors.ROYAL_BLUE.getIndex()); // 设置右边框; style.setBorderRight(BorderStyle.THIN); // 设置右边框颜色; style.setRightBorderColor(IndexedColors.ROYAL_BLUE.getIndex()); // 设置顶边框; style.setBorderTop(BorderStyle.THIN); // 设置顶边框颜色; style.setTopBorderColor(IndexedColors.ROYAL_BLUE.getIndex()); // 在样式用应用设置的字体; style.setFont(font); // 设置自动换行; style.setWrapText(false); // 设置水平对齐的样式为居中对齐; style.setAlignment(HorizontalAlignment.CENTER); // 设置垂直对齐的样式为居中对齐; style.setVerticalAlignment(VerticalAlignment.CENTER); return style; } }
整体项目结构如下
启动项目测试,点击导出
如果逻辑不清晰,建议跟着流程先把代码复制下去,实现了效果,自己在打断点跟流程
整理不易,感谢支持
学编程找星哥,么么哒