1. 背景

项目中使用到了导入导出的功能所以自己写了一个关于POI的工具类和demo,希望能帮到会使用到的朋友。对比了很多市面上的方法,最后还是选择了easyExcel,也把easyExcel的demo也写了,文章底部会有相关跳转连接,跳转到easyExcel的。

2.代码

2.1. maven依赖

        <dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>4.1.2</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>4.1.2</version></dependency>

2.2 工具类

package com.tencent.hr.corehr.utils;import cn.hutool.core.bean.BeanDesc;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.TypeUtil;
import com.tencent.hr.corehr.dto.ExcelRecord;
import com.tencent.hr.corehr.dto.ExcelResult;
import com.tencent.hr.corehr.rest.PositionController;
import org.apache.poi.hssf.usermodel.HSSFRichTextString;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFRichTextString;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.net.URLEncoder;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.locks.ReentrantLock;/*** @Author: v_qiicai* @date 2020/3/6*/
public class POIUtils {private static final Logger logger = LoggerFactory.getLogger(PositionController.class);/*** logger*/private static final Logger LOGGER = LoggerFactory.getLogger(POIUtils.class);private static ThreadLocal<List<RectangleArea>> mergeAreaList = new ThreadLocal<>();private static List<RectangleArea> getMergeAreaList() {return mergeAreaList.get();}private static void setMergeAreaList(List<RectangleArea> list) {mergeAreaList.set(list);}/*** 将单元格内容转换为字符串** @param cell* @return*/private static String convertCellValueToString(Cell cell) {if (cell == null) {return null;}String returnValue = null;switch (cell.getCellType()) {//数字case NUMERIC:if (org.apache.poi.ss.usermodel.DateUtil.isCellDateFormatted(cell)) {Date tempValue = cell.getDateCellValue();SimpleDateFormat simpleFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");returnValue = simpleFormat.format(tempValue);} else {Double doubleValue = cell.getNumericCellValue();// 格式化科学计数法,取一位整数DecimalFormat df = new DecimalFormat("0");returnValue = df.format(doubleValue);}break;//字符串case STRING:returnValue = cell.getStringCellValue();//布尔break;case BOOLEAN:Boolean booleanValue = cell.getBooleanCellValue();returnValue = booleanValue.toString();break;// 空值case BLANK:break;// 公式case FORMULA:returnValue = cell.getCellFormula();break;// 故障case ERROR:break;default:break;}return returnValue;}/*** 读取excel* 注意: 默认读取的数据是在第一个sheet中的,暂不支持多个sheet的读取** @param file       输入流* @param headRowNum 表头所在行数* @return 返回excel中的字符串数据* @throws Exception*/private static List<List<String>> readFile(File file, int headRowNum) throws Exception {InputStream ins = new FileInputStream(file);Workbook workbook = WorkbookFactory.create(ins);Sheet sheet = workbook.getSheetAt(0);// 获取excel总行数int rownum = sheet.getLastRowNum();List<List<String>> result = new ArrayList<>();LOGGER.info("excel 总行数:{}", rownum);// 获取表头那一行的数据长度short allColumnNum = sheet.getRow(headRowNum).getLastCellNum();for (int i = 0; i <= rownum; i++) {Row row = sheet.getRow(i);if (row == null || row.getPhysicalNumberOfCells() == 0) {LOGGER.info("第{}行数据为空.", i);continue;}List<String> oneRecord = new ArrayList<>();for (int j = 0; j < allColumnNum; j++) {Cell cell = row.getCell(j);
//                cell.setCellStyle(cellStyle);if (cell == null) {oneRecord.add("");continue;}String cellValue = convertCellValueToString(cell);oneRecord.add(cellValue);}result.add(oneRecord);}LOGGER.info("读取文件完成!");return result;}/*** 展示某些属性的方法** @param headers* @param list* @param useXSSF     是否使用2007* @param sheetName* @param includeAttr* @param isInclude* @return*/private static <T> Workbook getExcelWorkBook(String[] headers, List<T> list, boolean useXSSF, String sheetName, List<String> includeAttr, boolean isInclude) {Workbook workbook = getWorkbook(useXSSF);// 创建sheetSheet sheet = workbook.createSheet(sheetName);CellStyle cellStyle = getCellStyle(workbook);// 创建表头createHeader(headers, sheet, useXSSF, cellStyle);Field[] fields = null;// 确定行数for (int i = 0; i < list.size(); i++) {// 创建一行Row dataRow = sheet.createRow(i + 1);T t = list.get(i);if (isInclude) {// 为每个单元格设值for (int j = 0; j < includeAttr.size(); j++) {String fieldName = includeAttr.get(j);formatCell(sheet, cellStyle, dataRow, t, j, fieldName);}} else {fields = fields == null ? t.getClass().getDeclaredFields() : fields;// k用来解决某些字段并不在excel中展示带来的数据和列名不匹配问题for (int j = 0, k = 0; j < fields.length - 1; j++) {Field field = fields[j];String fieldName = field.getName();// flag标志代表是否不展示该项的值boolean flag = false;if (includeAttr.contains(fieldName)) {// 解决生成数据不匹配问题k++;flag = true;break;}if (flag) continue;formatCell(sheet, cellStyle, dataRow, t, j - k, fieldName);}}}return workbook;}/*** 格式化单元格** @param sheet* @param cellStyle* @param dataRow* @param t* @param j* @param fieldName* @param <T>*/private static <T> void formatCell(Sheet sheet, CellStyle cellStyle, Row dataRow, T t, int j, String fieldName) {Cell dataCell = dataRow.createCell(j);dataCell.setCellStyle(cellStyle);// 设置固定宽度, 每个单元格可存放16个字符sheet.setColumnWidth(j, 16 * 256);setCellValue(dataCell, ReflectionUtils.getFieldValue(t, fieldName));}/*** 获取通用style** @param workbook* @return*/private static CellStyle getCellStyle(Workbook workbook) {CellStyle cellStyle = workbook.createCellStyle();cellStyle.setBorderLeft(cellStyle.getBorderLeft());cellStyle.setLeftBorderColor(IndexedColors.BLACK.getIndex());cellStyle.setBorderRight(cellStyle.getBorderRight());cellStyle.setRightBorderColor(IndexedColors.BLACK.getIndex());cellStyle.setBorderBottom(cellStyle.getBorderBottom());cellStyle.setBottomBorderColor(IndexedColors.BLACK.getIndex());cellStyle.setBorderTop(cellStyle.getBorderTop());cellStyle.setTopBorderColor(IndexedColors.BLACK.getIndex());return cellStyle;}private static Workbook getWorkbook(boolean useXSSF) {return useXSSF ? new XSSFWorkbook() : new HSSFWorkbook();}/*** 给出字符串的情况下获取WorkBook** @param list* @return*/private static Workbook getExcelWorkBook(List<List<String>> list, boolean useXSSF) {Workbook workbook = getWorkbook(useXSSF);// 创建sheet, 名字默认为sheet0Sheet sheet = workbook.createSheet("sheet0");// 确定行数for (int i = 0; i < list.size(); i++) {// 创建一行Row dataRow = sheet.createRow(i);List<String> t = list.get(i);// 为每个单元格设值for (int j = 0; j < t.size(); j++) {Cell dataCell = dataRow.createCell(j);// 设置固定宽度, 每个单元格可存放16个字符setCellValue(dataCell, t.get(j));}}return workbook;}/*** 创建表头** @param headers* @param sheet*/private static void createHeader(String[] headers, Sheet sheet, boolean useXSSF, CellStyle commonStyle) {Row headerRow = sheet.createRow(0);for (int i = 0; i < headers.length; i++) {Cell headerRowCell = headerRow.createCell(i);headerRowCell.setCellStyle(commonStyle);RichTextString text = useXSSF ? new XSSFRichTextString(headers[i]) : new HSSFRichTextString(headers[i]);headerRowCell.setCellValue(text);}}/*** 为单元格设置值** @param dataCell* @param value*/private static void setCellValue(Cell dataCell, Object value) {if (value instanceof Date) {DateFormat sdf = DateUtil.secondFormat.get();String zero = "00:00:00";if (zero.equals(sdf.format(value).split(" ")[1])) {sdf = DateUtil.dayFormat.get();}dataCell.setCellValue(sdf.format(value));} else if (value instanceof Number) {dataCell.setCellValue(Double.parseDouble(String.valueOf(value)));} else if (value == null) {dataCell.setCellValue("");} else {dataCell.setCellValue(value.toString());}}/*** 导出excel ——展示某些属性** @param title       导出名字* @param headers     表头数组* @param list        数据* @param response    写入体* @param useXSSF     是否使用2007Excel* @param sheetName   创建sheet名字* @param includeAttr 展示实体的哪些属性*/private static void exportExcel(String title, String[] headers, List<?> list, HttpServletResponse response, boolean useXSSF, String sheetName, List<String> includeAttr, boolean isInclude) {try {Workbook workbook = getExcelWorkBook(headers, list, useXSSF, sheetName, includeAttr, isInclude);addMergeArea(workbook, 0, getMergeAreaList());writeToResp(title, workbook, response, useXSSF);} catch (Exception e) {e.printStackTrace();}}/*** 给出导出字符串的情况下的导出方法** @param title    导出名字* @param list     表头+数据* @param response 写入体*/private static void exportExcel(String title, List<List<String>> list, boolean useXSSF, HttpServletResponse response) {try {Workbook workbook = getExcelWorkBook(list, useXSSF);writeToResp(title, workbook, response, useXSSF);} catch (Exception e) {e.printStackTrace();}}/*** 给出导出字符串的情况下的导出方法** @param title 导出名字* @param list  表头+数据*/private  void exportExcel(String title, List<List<String>> list, boolean useXSSF) {try {Workbook workbook = getExcelWorkBook(list, useXSSF);CellStyle cellStyle = workbook.createCellStyle();cellStyle.setBorderBottom(BorderStyle.THIN); // 下边框cellStyle.setBorderLeft(BorderStyle.THIN);// 左边框cellStyle.setBorderTop(BorderStyle.THIN);// 上边框cellStyle.setBorderRight(BorderStyle.THIN);// 右边框cellStyle.setAlignment(HorizontalAlignment.CENTER);// 水平居中Font font = workbook.createFont();font.setFontName("微软雅黑");// 设置字体名称font.setFontHeightInPoints((short) 10);// 设置字号font.setColor(IndexedColors.BLACK.index);// 设置字体颜色cellStyle.setFont(font);FileOutputStream fileOut;try {fileOut = new FileOutputStream("D:/test/" + title + ".xlsx");workbook.write(fileOut);fileOut.close();} catch (Exception e) {e.printStackTrace();}System.out.println("写出成功!");} catch (Exception e) {e.printStackTrace();}}/*** 将excel流写入response** @param title* @param workbook* @param response* @param useXSSF*/private static void writeToResp(String title, Workbook workbook, HttpServletResponse response, boolean useXSSF) {ServletOutputStream outputStream = null;try {LOGGER.info("" + workbook);outputStream = response.getOutputStream();// 解决浏览器及中文乱码问题https://tools.ietf.org/html/rfc2231response.setHeader("Content-Disposition", "attachment;filename*=utf-8'zh_cn'" + URLEncoder.encode(title, "UTF-8") + (useXSSF ? ".xlsx" : ".xls"));response.setContentType("application/msexcel");workbook.write(outputStream);} catch (Exception e) {e.printStackTrace();} finally {// 关闭流资源StreamUtil.close(outputStream, workbook);}}/*** 合并单元格并水平垂直居中实现** @param areas*/private static void addMergeArea(Workbook workbook, int sheetIndex, List<RectangleArea> areas) {ReentrantLock lock = new ReentrantLock();try {lock.lock();LOGGER.info("Thread: {}, threadLocal is: {}.", Thread.currentThread().getName(), String.valueOf(getMergeAreaList()));if (areas != null && areas.size() > 0) {for (RectangleArea area : areas) {workbook.getSheetAt(sheetIndex).addMergedRegion(new CellRangeAddress(area.getFirstRow(), area.getLastRow(), area.getFirstCol(), area.getLastCol()));// 合并单元格时默认居中(水平、垂直居中)CellStyle cellStyle = getCellStyle(workbook);cellStyle.setAlignment(cellStyle.getAlignment());cellStyle.setVerticalAlignment(cellStyle.getVerticalAlignment());workbook.getSheetAt(sheetIndex).getRow(area.getFirstRow()).getCell(area.getFirstCol()).setCellStyle(cellStyle);}mergeAreaList.set(null);}} finally {lock.unlock();}}private static class RectangleArea {private int firstRow, lastRow, firstCol, lastCol;private RectangleArea(int firstRow, int lastRow, int firstCol, int lastCol) {this.firstRow = firstRow;this.lastRow = lastRow;this.firstCol = firstCol;this.lastCol = lastCol;}private int getFirstRow() {return firstRow;}private int getLastRow() {return lastRow;}private int getFirstCol() {return firstCol;}private int getLastCol() {return lastCol;}}private static <T> List<T> poiToDto(T t, List<List<String>> parentList) throws InvocationTargetException, IllegalAccessException {List<T> returnList = new ArrayList<>();//循环行,从第二行开始取值因为第一行是表头for (int j = 1; j < parentList.size(); j++) {final Collection<BeanDesc.PropDesc> props = BeanUtil.getBeanDesc(t.getClass()).getProps();//列数int i = 0;//循环对象里的属性按顺序赋值for (BeanDesc.PropDesc prop : props) {Method setterMethod = prop.getSetter();if (null == setterMethod) {// Setter方法不存在跳过continue;}Object value;//取得参数类型Type firstParamType = TypeUtil.getFirstParamType(setterMethod);//取得参数类型名称并且进行类型转换String typeName = firstParamType.getTypeName();switch (typeName) {//数字case "java.lang.Integer":value = Integer.valueOf(parentList.get(j).get(i));break;//字符串case "java.lang.String":value = String.valueOf(parentList.get(j).get(i));break;case "java.sql.Timestamp":value = DateUtil.stringToTimestamp(parentList.get(j).get(i));break;default:value = null;break;}//赋值setterMethod.invoke(t, value);i++;}//插入DTOreturnList.add(t);}return returnList;}public static ExcelResult poiToExcelResult(List<String> header, File file,int startLine ) throws Exception {List<List<String>> parentList = readFile(file,startLine);ExcelResult excelResult = new ExcelResult();//如果请求头和表头不一致if (!ListUtils.isEquals(header, parentList.get(startLine-1))) {excelResult.setSuccess(false);excelResult.setMsg("列表表头不一致");logger.error("列表表头不一致");return excelResult;}List<ExcelRecord> Data = new ArrayList<>();for (int i = startLine; i < parentList.size(); i++) {ExcelRecord excelRecord = new ExcelRecord();//表头Map<String, Integer> mapping = new HashMap<>(16);//valueList<Object> objects = new ArrayList<>();//列数Integer recordNumber = 0;for (int j = 0; j < parentList.get(i).size(); j++) {//设置表头mapping.put(parentList.get(startLine).get(j), j);//设置valueobjects.add(parentList.get(i).get(j));//设置列数recordNumber++;}excelRecord.setMapping(mapping);excelRecord.setObjects(objects);excelRecord.setRecordNumber(recordNumber);Data.add(excelRecord);}excelResult.setNumber(parentList.size() - 1);excelResult.setData(Data);excelResult.setSuccess(true);excelResult.setMsg("OK");return excelResult;}}

2.3 使用demo

放在test下即可运行

package com.tencent.hr.corehr;import com.tencent.hr.corehr.dto.ExcelResult;
import com.tencent.hr.corehr.utils.POIUtils;
import org.junit.Test;
import org.springframework.boot.test.context.SpringBootTest;import java.io.*;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.*;@SpringBootTest
public class POIUtilsDemo {/*** 读取Excel*/@Testpublic void readExcel() throws Exception {String filePath = "D:/test/新增专业职级.xlsx";File file = new File(filePath);
//List<String> header = Arrays.asList("专业职级名称", "生效日期", "描述");
//        List<String> header2=Arrays.asList("序号", "姓名", "性别","生日");
//        boolean equals = ListUtils.isEquals(header, header2);ExcelResult excelResult = POIUtils.poiToExcelResult(header, file, 4);System.out.println(excelResult.toString());}/*** 导出字符串的情况下的导出方法*/public List<Employee> generateSampleEmployeeData() throws Exception {List<Employee> employees = new ArrayList<Employee>();SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MMM-dd", Locale.US);employees.add(new Employee("Elsa", dateFormat.parse("1970-Jul-10"), 1500, 0.15));employees.add(new Employee("Oleg", dateFormat.parse("1973-Apr-30"), 2300, 0.25));employees.add(new Employee("Neil", dateFormat.parse("1975-Oct-05"), 2500, 0.00));employees.add(new Employee("Maria", dateFormat.parse("1978-Jan-07"), 1700, 0.15));employees.add(new Employee("John", dateFormat.parse("1969-May-30"), 2800, 0.20));return employees;}}class testDTO {private Integer ID;private String name;private String gender;private Timestamp birthDay;public Integer getID() {return ID;}public void setID(Integer ID) {this.ID = ID;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getGender() {return gender;}public void setGender(String gender) {this.gender = gender;}public Timestamp getBirthDay() {return birthDay;}public void setBirthDay(Timestamp birthDay) {this.birthDay = birthDay;}@Overridepublic String toString() {return "testDTO{" +"ID=" + ID +", name='" + name + '\'' +", gender='" + gender + '\'' +", birthDay=" + birthDay +'}';}
}class Employee {private String name;private Date birthDate;private BigDecimal payment;private BigDecimal bonus;public Employee(String john, Date parse, int i, double v) {}public Employee(String name, Date birthDate, BigDecimal payment, BigDecimal bonus) {this.name = name;this.birthDate = birthDate;this.payment = payment;this.bonus = bonus;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Date getBirthDate() {return birthDate;}public void setBirthDate(Date birthDate) {this.birthDate = birthDate;}public BigDecimal getPayment() {return payment;}public void setPayment(BigDecimal paym) {this.payment = payment;}public BigDecimal getBonus() {return bonus;}public void setBonus(BigDecimal bonus) {this.bonus = bonus;}
}

《easyExcel结合Springboot 实战整合》
https://blog.csdn.net/BCDMW233/article/details/105225284

实用 POI工具类(Spring boot)相关推荐

  1. 带有第三方工具的Spring Boot Initilizr

    This is continuation to my two previous posts. Before reading this post, please go through my previo ...

  2. 基于jdk8 LocalDate系列API的全新实用时间工具类

    基于jdk8 LocalDate系列API的实用时间工具类, 已经经过多个项目的考验与完善, 包含个人心得体会 欢迎转载,转载请注明网址:https://blog.csdn.net/qq_419102 ...

  3. 利用POI工具类实现导出Excel的功能

    poi工具类的概述: Apache POI是Apache软件基金会的开放源码函式库,POI提供API给Java程序对Microsoft Office格式档案读和写的功能. 结构: HSSF - 提供读 ...

  4. 【星云测试】开发者测试-采用精准测试工具对Spring Boot应用进行测试

    2019独角兽企业重金招聘Python工程师标准>>> 简介:本文主要介绍把现今主流的springboot框架项目和精准测试工具进行结合和应用,通过精准测试的数据穿透.数据采集.测试 ...

  5. 绝了!这款工具让 Spring Boot 不在需要 Controller、Service、DAO、Mapper 了

    ‍ 来源:my.oschina.net/ta8210/blog/3234639 Dataway 是基于 DataQL 服务聚合能力,为应用提供的一个接口配置工具.使得使用者无需开发任何代码就配置一个满 ...

  6. 测开工具:spring boot 实现mock平台

    目录 一.实现功能 1.使用spring boot 实现mock平台 2.返回结果数据的存放: 3.如何根据url返回对应的结果? 1.3.1  将请求的URI拼成返回结果的文件/文件夹路径 1.3. ...

  7. java使用POI工具类导出excel

    POI导出excel 1.导入maven依赖 <dependency><groupId>org.apache.poi</groupId><artifactId ...

  8. 记录常用的代码工具(二)--Poi工具类

    这里用的是apache的poi,提供API给Java程序对MicrosoftOffice格式档案读和写的功能,其中使用最多的就是使用POI操作Excel文件 依赖的jar包: <dependen ...

  9. POI工具类(终极一版)分别支持两个版本的

    只需要传入 之前的需要追加的 Workbook对象,如果不需要追加 传NULL即可(默认情况下用的HSSF),传入 list 跟 sheet名称 ,tableName ,为 list所在区域的名称.工 ...

最新文章

  1. npm : 无法加载文件 D:\...\nodejs\npm.ps1,因为在此系统上禁止运行脚本
  2. mysql img格式缩放,再学 GDI+[89]: TGPImage(9) - 图像缩放时的质量(算法)
  3. VirtualBox虚拟机后台运行
  4. 微信小程序开发第三弹
  5. 2018年安卓绿色联盟数据报告
  6. 火星今天飞抵西非国家寻找埃博拉疫情
  7. 读书笔记_CLR.via.c#第十六章_数组
  8. RabbitMQ和Kafka的区别
  9. 重写toString()方法(Java篇)
  10. 数码相机控制点的自动定位检校
  11. Mockito 101
  12. java猜数游戏图形界面_Java做一个猜数的小游戏
  13. 码说 | 并查集(以HDU1232为例)
  14. Java\学习——字符串
  15. 监护仪系统都是Linux吗,基于Linux和MiniGUI的心电监护仪设计 (1)
  16. leetcode 21 java_LeetCode 21. 合并两个有序链表
  17. 【第24章】工控安全需求分析与安全保护工程(软考:信息安全工程师)-- 学习笔记
  18. premiere小tips(参考于干的教程)
  19. MATLAB 粒子群算法,例题与常用模版
  20. 手机新闻网站,手持移动新闻,手机报client,jQuery Mobile手机新闻网站,手机新闻网站demo,新闻阅读器开发...

热门文章

  1. Python函数及变量详解
  2. 我的Linux PC开发环境
  3. Sql server 索引碎片
  4. (BDCI-CCF)出租车发票识别
  5. 关于MVVM与MVI
  6. stm32 f1和f4的比较
  7. java如何给视频添加水印logo
  8. 真品篮球鞋与仿真鞋的分别
  9. 云原生之容器编排实践-在CentOS7上安装minikube
  10. java-古堡谜题-第一阶段