实用 POI工具类(Spring boot)
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)相关推荐
- 带有第三方工具的Spring Boot Initilizr
This is continuation to my two previous posts. Before reading this post, please go through my previo ...
- 基于jdk8 LocalDate系列API的全新实用时间工具类
基于jdk8 LocalDate系列API的实用时间工具类, 已经经过多个项目的考验与完善, 包含个人心得体会 欢迎转载,转载请注明网址:https://blog.csdn.net/qq_419102 ...
- 利用POI工具类实现导出Excel的功能
poi工具类的概述: Apache POI是Apache软件基金会的开放源码函式库,POI提供API给Java程序对Microsoft Office格式档案读和写的功能. 结构: HSSF - 提供读 ...
- 【星云测试】开发者测试-采用精准测试工具对Spring Boot应用进行测试
2019独角兽企业重金招聘Python工程师标准>>> 简介:本文主要介绍把现今主流的springboot框架项目和精准测试工具进行结合和应用,通过精准测试的数据穿透.数据采集.测试 ...
- 绝了!这款工具让 Spring Boot 不在需要 Controller、Service、DAO、Mapper 了
来源:my.oschina.net/ta8210/blog/3234639 Dataway 是基于 DataQL 服务聚合能力,为应用提供的一个接口配置工具.使得使用者无需开发任何代码就配置一个满 ...
- 测开工具:spring boot 实现mock平台
目录 一.实现功能 1.使用spring boot 实现mock平台 2.返回结果数据的存放: 3.如何根据url返回对应的结果? 1.3.1 将请求的URI拼成返回结果的文件/文件夹路径 1.3. ...
- java使用POI工具类导出excel
POI导出excel 1.导入maven依赖 <dependency><groupId>org.apache.poi</groupId><artifactId ...
- 记录常用的代码工具(二)--Poi工具类
这里用的是apache的poi,提供API给Java程序对MicrosoftOffice格式档案读和写的功能,其中使用最多的就是使用POI操作Excel文件 依赖的jar包: <dependen ...
- POI工具类(终极一版)分别支持两个版本的
只需要传入 之前的需要追加的 Workbook对象,如果不需要追加 传NULL即可(默认情况下用的HSSF),传入 list 跟 sheet名称 ,tableName ,为 list所在区域的名称.工 ...
最新文章
- npm : 无法加载文件 D:\...\nodejs\npm.ps1,因为在此系统上禁止运行脚本
- mysql img格式缩放,再学 GDI+[89]: TGPImage(9) - 图像缩放时的质量(算法)
- VirtualBox虚拟机后台运行
- 微信小程序开发第三弹
- 2018年安卓绿色联盟数据报告
- 火星今天飞抵西非国家寻找埃博拉疫情
- 读书笔记_CLR.via.c#第十六章_数组
- RabbitMQ和Kafka的区别
- 重写toString()方法(Java篇)
- 数码相机控制点的自动定位检校
- Mockito 101
- java猜数游戏图形界面_Java做一个猜数的小游戏
- 码说 | 并查集(以HDU1232为例)
- Java\学习——字符串
- 监护仪系统都是Linux吗,基于Linux和MiniGUI的心电监护仪设计 (1)
- leetcode 21 java_LeetCode 21. 合并两个有序链表
- 【第24章】工控安全需求分析与安全保护工程(软考:信息安全工程师)-- 学习笔记
- premiere小tips(参考于干的教程)
- MATLAB 粒子群算法,例题与常用模版
- 手机新闻网站,手持移动新闻,手机报client,jQuery Mobile手机新闻网站,手机新闻网站demo,新闻阅读器开发...