java 工具|使用 POI 导入导出 EXCEL

本贴最后更新于 1910 天前,其中的信息可能已经斗转星移

javaDEMO

Java 基础 Demo 站: https://www.javastudy.cloud
Java 中高级开发博客: https://www.lixiang.red
Java 学习公众号: java 技术大本营
java_subscribe

POI 简介

在 java 中,对 word/Excel 的处理,基本上都是通过 poi 来完成的
poi 地址:
https://poi.apache.org/components/spreadsheet/index.html
Word/Excel 分两大版本,对应着我们 doc/xls 和 docx/xlsx 这些后缀名,其中的 x 是代表 xml 的意思. 就是说, 新版的 Word/Excel 都可以用 XML 来表式.
使用场景:在用 word+freemarker 做模板时, 先在 word 中,用{model}占位符. 然后把 word 另存为 xml 文档. 然后在 freemarker 中,读取这个 xml 文档,然后传入 map,最后输出流再转成 word 就可以了.
当然在实际开发中,还是 Excel 用的多. 用来导入和导出数据.要使用 poi 处理 EXCEL 需要以下步骤:
1.引入相关的依赖
2. 定义实体类和 Excel 表头的关联
3. 编写 Excel 操作的工具类
3. 测试

POI 导入/导出 EXCEL

引入相关的依赖

compile group: 'org.apache.poi', name: 'poi', version: '4.1.1' compile group: 'org.apache.poi', name: 'poi-ooxml', version: '4.1.1'

定义实体类和 Excel 表头的关联

这里我定义一个 CellField 来描述这种关系,其结构如下所示

public class CellField { /** cell 值,字符串 */ public static final int FIELD_STRING = 1; /** cell类型, 普通的 */ public static final int CELL_NORMAL = 1; /** cell类型, 下拉框 * 下拉框的中的java字段一定要和实体类的值对应,不然不好set进去,而且最好是和String类型的对应 */ public static final int CELL_DROP = 2; /** java实体类名字 */ private String javaName; /** excel表头的东西 */ private String cellName; /** cell值的类型,有字符串,日期,等等,默认是字符串的 */ private Integer fieldType = 1; /** cell 的类型, 有普通的,下拉框,等等,默认是普通的 */ private Integer cellType = 1; /** 排序值,默认是0 */ private Integer order = 0; /** 默认值 */ private Object defaultValue; public CellField() { } public CellField(String javaName, String cellName) { this.javaName = javaName; this.cellName = cellName; } public CellField(String javaName, String cellName, Integer cellType) { this.javaName = javaName; this.cellName = cellName; this.cellType = cellType; } public String getJavaName() { return javaName; } public CellField setJavaName(String javaName) { this.javaName = javaName; return this; } public String getCellName() { return cellName; } public CellField setCellName(String cellName) { this.cellName = cellName; return this; } public Integer getFieldType() { return fieldType; } public CellField setFieldType(Integer fieldType) { this.fieldType = fieldType; return this; } public Integer getCellType() { return cellType; } public CellField setCellType(Integer cellType) { this.cellType = cellType; return this; } public Integer getOrder() { return order; } public CellField setOrder(Integer order) { this.order = order; return this; } public Object getDefaultValue() { return defaultValue; } public CellField setDefaultValue(Object defaultValue) { this.defaultValue = defaultValue; return this; } }

POI 操作 Excel 的几个基础方法

/** * 创建Excel的表头 */ public static Row createExcelHeader(List<CellField> cellFields){ Workbook wb = new XSSFWorkbook(); return createExcelHeader( wb.createSheet(),cellFields); } public static Row createExcelHeader(Sheet sheet,List<CellField> cellFields){ cellFields.sort(Comparator.comparingInt(CellField::getOrder)); Map<String, CellField> paramMap = cellFields.stream().collect(Collectors.toMap(CellField::getJavaName, x -> x,(x,y)-> x,LinkedHashMap::new)); int colPointer = 0; // 渲染第一行的表头 Row firstRow = accessRow(sheet, 0); for (CellField field : cellFields) { String fieldName = field.getJavaName(); if (paramMap.get(fieldName) != null) { Cell cell = accessCell(firstRow, colPointer++); // System.out.println(fieldName + "!!!!" + paramMap.get(fieldName)); cell.setCellValue(paramMap.get(fieldName).getCellName()); if(CELL_DROP==field.getCellType()){ createDropList((List<String>) field.getDefaultValue(),cell,sheet); } } } return firstRow; } /** * 对excel创建下拉框 * @param list * @param cell * @param sheet */ public static void createDropList(List<String> list, Cell cell, Sheet sheet) { CellRangeAddressList addressList = new CellRangeAddressList(1, 2000, cell.getColumnIndex(), cell.getColumnIndex()); DataValidationHelper dvHelper = sheet.getDataValidationHelper(); DataValidationConstraint dvConstraint = dvHelper.createExplicitListConstraint(list.toArray(new String[0])); DataValidation validation = dvHelper.createValidation(dvConstraint, addressList); sheet.addValidationData(validation); } /** * 导出Excel文件 * @param wb * @return */ public static void exportExcelFile(Workbook wb, String partFileName) { if (wb != null) { String fileName = "Excel-" + partFileName + "-" + new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss").format(new Date()) + ".xlsx"; OutputStream out = null; try { out = new FileOutputStream(new File(fileName)); wb.write(out); } catch (IOException e) { e.printStackTrace(); } finally { try { if(out!=null){ out.close(); } } catch (IOException e) { e.printStackTrace(); } } } } /*** * 创建一行 * @param sheet * @param rowNum * @return */ private static Row accessRow(Sheet sheet, int rowNum) { Row row = sheet.getRow(rowNum); if (row == null) { row = sheet.createRow(rowNum); } return row; } /** * 创建一个单元格 * * @param row * @param column * @return */ private static Cell accessCell(Row row, int column) { Cell cell = row.getCell(column); if (cell == null) { cell = row.createCell(column); } return cell; }

POI 把 List 导出成 Excel

/** * 将List对象转换为excel文档. * * @param list POJO对象列表 * @param cellFields 表头的描述项 * @throws Exception */ public static <T> Workbook beanToExcel(List<T> list, List<CellField> cellFields, Class<T> clazz) { cellFields.sort(Comparator.comparingInt(CellField::getOrder)); Map<String, CellField> paramMap = cellFields.stream().collect(Collectors.toMap(CellField::getJavaName, x -> x,(x,y)-> x,LinkedHashMap::new)); XSSFWorkbook workbook = new XSSFWorkbook(); XSSFSheet sheet = workbook.createSheet(); // 处理要反射的字段 Field[] fields = clazz.getDeclaredFields(); Map<String, Field> fieldMap = new HashMap<>(fields.length); for (Field field : fields) { field.setAccessible(true); String fieldName = field.getName(); fieldMap.put(fieldName, field); } // 渲染第一行的表头 Row firstRow = createExcelHeader(sheet,cellFields); int beginNum = 1; for (T d : list) { Row row = accessRow(sheet, beginNum++); for (int i = 0; i < paramMap.size(); i++) { //开始为每行写入值 try { for (int i1 = 0; i1 < cellFields.size(); i1++) { //获取表头对应的java名 CellField cellField = cellFields.get(i1); String javaName = cellField.getJavaName(); String cellHeaderName = cellField.getCellName(); if (javaName != null && cellHeaderName.equals(firstRow.getCell(i).getStringCellValue())) { Field field = fieldMap.get(javaName); Cell cell = accessCell(row, i); if(field ==null){ //对于CellFieldList里面有的字段,实体类中没有的,跳过这一列 cell.setBlank(); continue; } Object obj = field.get(d); if (obj == null) { continue; } else if (String.class.isAssignableFrom(obj.getClass())) { cell.setCellValue((String) obj); } else if (Date.class.isAssignableFrom(obj.getClass())) { // 日期存储为Number,显示需求依赖CellStyle cell.setCellValue((Date) obj); } else if (Number.class.isAssignableFrom(obj.getClass())) { // 数字类型的也都按字符串方式来存 cell.setCellValue(String.valueOf(obj)); } else if (Boolean.class.isAssignableFrom(obj.getClass())) { cell.setCellValue(((Boolean) obj)); } else { cell.setCellValue(obj.toString()); } } } } catch (IllegalAccessException e) { e.printStackTrace(); } } } return workbook; }

POI 读取 Excel 为 List

public static <T> List<T> excelToList(InputStream inputStream,List<CellField> cellFieldList, Class<T> clazz){ List<T> list = new ArrayList<>(); //hashMap是以键值对(key and value)来存储的 //<列名,单元格值> HashMap<String, String> indexMap = new HashMap<>(); try { //fileInputStream就是一个book XSSFWorkbook xssfWorkbook = new XSSFWorkbook(inputStream); //从book中获取第一个sheet XSSFSheet xssfSheet = xssfWorkbook.getSheetAt(0); //获取sheet的总行数 int rows = xssfSheet.getPhysicalNumberOfRows(); //规定第1(引索是0)行是字段名行 XSSFRow firstRow = xssfSheet.getRow(0); //遍历是从第2(引索是1)行开始 for (int i = 1; i < rows; i++) { //从整个sheet里面获取第i行的数据 XSSFRow xssfRow = xssfSheet.getRow(i); if (xssfRow == null) { //如果某一行为空 就跳过读取下一行 continue; } //读取一行中的每一个单元格 for (int j = 0; j < xssfRow.getLastCellNum(); j++) { //获取该单元格的列名 //getStringCellValue从单元格取出类型为String的值 String columnCellName = firstRow.getCell(j).getStringCellValue(); //开始从第一列开始遍历单元格 XSSFCell cell = xssfRow.getCell(j); String value = ""; if (cell != null) { switch (cell.getCellType()) { case FORMULA: value = cell.getCellFormula(); break; case NUMERIC: value = "" + cell.getNumericCellValue(); break; case STRING: value = cell.getStringCellValue(); break; case BLANK: value = "<BLANK>"; break; case BOOLEAN: value = "" + cell.getBooleanCellValue(); break; case ERROR: value = "" + cell.getErrorCellValue(); break; default: value = "" + cell.getStringCellValue(); } } indexMap.put(columnCellName,value); } //一行循环之后,将值注入到新生成的实例中,然后放到list中 try { T t = clazz.newInstance(); //获取到传入Class的全部变量名 // 对CellList进行循环, 然后赋值 for (CellField cellField : cellFieldList) { String javaName = cellField.getJavaName(); String columnName = cellField.getCellName(); try{ Field field = clazz.getDeclaredField(javaName); field.setAccessible(true); if(field.getType()==Integer.class){ if(indexMap.get(columnName)!=null){ double d = Double.valueOf(indexMap.get(columnName)); field.set(t, (int)d); } }else if(field.getType()==Double.class) { if(indexMap.get(columnName)!=null){ double d = Double.valueOf(indexMap.get(columnName)); field.set(t, d); } }else if(field.getType()== BigDecimal.class) { if(indexMap.get(columnName)!=null){ BigDecimal b = new BigDecimal(indexMap.get(columnName)); field.set(t, b); } }else { field.set(t, indexMap.get(columnName)); } }catch (NoSuchFieldException e){ // 如果发生了这个异常,说明没有对应的java字段,可能是下拉框 // 对异常不做处理,也就是对这个不做处理 } catch (IllegalAccessException e) { e.printStackTrace(); } } list.add(t); } catch (InstantiationException | IllegalAccessException e) { e.printStackTrace(); } } } catch (IOException e) { e.printStackTrace(); } return list; }

DEMO 总评

Java 操作 Excel 基本上都是通过 POI,有很多人有不同的封装, 大体上思路都是差不多的.本 DEMO 的思路也仅供参考,两三天简单封装了一下, 考虑的也不是太全面.欢迎小伙伴们一起完善,加油!

  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3195 引用 • 8215 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...