概述

POI

简介

Apache POI是Apache软件基金会的开放源码函式库,POI提供API给Java程序对Microsoft Office格式档案读和写的功能。

功能

HSSF:提供读写Microsoft Excel格式档案的功能。
XSSF:提供读写Microsoft Excel OOXML格式档案的功能。
HWPF:提供读写Microsoft Word格式档案的功能。
HSLF:提供读写Microsoft PowerPoint格式档案的功能。
HDGF:提供读写Microsoft Visio Visio格式档案的功能。

EasyExcel

简介

EasyExcel是alibaba的一个基于Java的简单、省内存的读写Excel的开源项目。

文档

yuque: https://www.yuque.com/easyexcel/doc/easyexcel
github: https://github.com/alibaba/easyexcel

POI

dependency

  1. <!-- test -->
  2. <dependency>
  3. <groupId>junit</groupId>
  4. <artifactId>junit</artifactId>
  5. <version>4.12</version>
  6. </dependency>
  7. <!-- xls(2003) -->
  8. <dependency>
  9. <groupId>org.apache.poi</groupId>
  10. <artifactId>poi</artifactId>
  11. <version>3.14</version>
  12. </dependency>
  13. <!-- xlsx(2007) -->
  14. <dependency>
  15. <groupId>org.apache.poi</groupId>
  16. <artifactId>poi-ooxml</artifactId>
  17. <version>3.14</version>
  18. </dependency>
  19. <!-- jodatime -->
  20. <dependency>
  21. <groupId>joda-time</groupId>
  22. <artifactId>joda-time</artifactId>
  23. <version>2.10.8</version>
  24. </dependency>

write

HSSF创建xls(2003版本)

  1. /**
  2. * <p>HSSF创建xls</p>
  3. * @throws Exception
  4. */
  5. @Test
  6. public void testWrite03() throws Exception {
  7. // 1、创建一个工作簿
  8. Workbook workbook = new HSSFWorkbook();
  9. // 2、创建一个工作表
  10. Sheet sheet = workbook.createSheet("K1");
  11. // 3、创建第一行数据
  12. Row row1 = sheet.createRow(0);
  13. // 4、创建两个单元格
  14. Cell cell11 = row1.createCell(0);
  15. cell11.setCellValue("ID");
  16. Cell cell12 = row1.createCell(1);
  17. cell12.setCellValue(1);
  18. // 5、创建第二行数据
  19. Row row2 = sheet.createRow(1);
  20. Cell cell21 = row2.createCell(0);
  21. cell21.setCellValue("姓名");
  22. Cell cell22 = row2.createCell(1);
  23. cell22.setCellValue("K1");
  24. // 6、创建第三行数据
  25. Row row3 = sheet.createRow(2);
  26. Cell cell31 = row3.createCell(0);
  27. cell31.setCellValue("时间");
  28. Cell cell32 = row3.createCell(1);
  29. cell32.setCellValue(new DateTime().toString("yyyy-MM-dd HH:mm:ss"));
  30. // 7、生成一张表
  31. FileOutputStream fileOutputStream = new FileOutputStream(System.getProperty("user.dir") + "/file/统计表03.xls");
  32. workbook.write(fileOutputStream);
  33. fileOutputStream.close();
  34. }

XSSF创建xlsx(2007版本)

  1. @Test
  2. public void testWrite07() throws Exception {
  3. // 1、创建一个工作簿
  4. Workbook workbook = new XSSFWorkbook();
  5. // 2、创建一个工作表
  6. Sheet sheet = workbook.createSheet("K1");
  7. // 3、创建第一行数据
  8. Row row1 = sheet.createRow(0);
  9. // 4、创建两个单元格
  10. Cell cell11 = row1.createCell(0);
  11. cell11.setCellValue("ID");
  12. Cell cell12 = row1.createCell(1);
  13. cell12.setCellValue(1);
  14. // 5、创建第二行数据
  15. Row row2 = sheet.createRow(1);
  16. Cell cell21 = row2.createCell(0);
  17. cell21.setCellValue("姓名");
  18. Cell cell22 = row2.createCell(1);
  19. cell22.setCellValue("K1");
  20. // 6、创建第三行数据
  21. Row row3 = sheet.createRow(2);
  22. Cell cell31 = row3.createCell(0);
  23. cell31.setCellValue("时间");
  24. Cell cell32 = row3.createCell(1);
  25. cell32.setCellValue(new DateTime().toString("yyyy-MM-dd HH:mm:ss"));
  26. // 7、生成一张表
  27. FileOutputStream fileOutputStream = new FileOutputStream(System.getProperty("user.dir") + "/file/统计表07.xlsx");
  28. workbook.write(fileOutputStream);
  29. fileOutputStream.close();
  30. }

注意区别

  • 对象不同,03版本是HSSFWorkBook,07版本是XSSFWorkBook
  • 文件后缀,03版本excel后缀是xls,07版本excel后缀是xlsx

大文件写HSSF

缺点:最多只能处理65536行,否则会抛异常
优点:过程中写入缓存,不操作磁盘,最后一次性写入磁盘,速度快

  1. @Test
  2. public void testWrite03BigData() throws Exception{
  3. // 开始时间
  4. long start = System.currentTimeMillis();
  5. // 创建工作簿
  6. Workbook workbook = new HSSFWorkbook();
  7. // 创建一张表
  8. Sheet sheet = workbook.createSheet();
  9. // 写入数据
  10. for (int rowNum = 0; rowNum < 65537; rowNum++) {
  11. Row row = sheet.createRow(rowNum);
  12. for (int cellNum = 0; cellNum < 10; cellNum++) {
  13. Cell cell = row.createCell(cellNum);
  14. cell.setCellValue(cellNum);
  15. }
  16. }
  17. FileOutputStream fileOutputStream = new FileOutputStream(System.getProperty("user.dir") + "/file/testWrite03BigData.xls");
  18. workbook.write(fileOutputStream);
  19. fileOutputStream.close();
  20. long end = System.currentTimeMillis();
  21. System.out.println("耗时:" + (end - start) + "ms");
  22. }

大文件写XSSF

缺点:写数据时速度非常慢,非常耗内存,也会发生内存溢出,如100万条
优点:可以写较大的数据量,如20万条

@Test
public void testWrite07BigData() throws Exception{
    // 开始时间
    long start = System.currentTimeMillis();
    // 创建工作簿
    Workbook workbook = new XSSFWorkbook();
    // 创建一张表
    Sheet sheet = workbook.createSheet();
    // 写入数据
    for (int rowNum = 0; rowNum < 65537; rowNum++) {
        Row row = sheet.createRow(rowNum);
        for (int cellNum = 0; cellNum < 10; cellNum++) {
            Cell cell = row.createCell(cellNum);
            cell.setCellValue(cellNum);
        }
    }
    FileOutputStream fileOutputStream = new FileOutputStream(System.getProperty("user.dir") + "/file/testWrite07BigData.xlsx");
    workbook.write(fileOutputStream);
    fileOutputStream.close();
    long end = System.currentTimeMillis();
    System.out.println("耗时:" + (end - start) + "ms");
}

大文件写SXSSF

优点:可以写非常大的数据量,如100万条甚至更多条,写数据速度快,占用更少的内存
注意:
过程中会产生临时文件,需要清理临时文件。
默认由100条记录被保存在内存中,如果超过这数量,则最前面的数据被写入临时文件。
如果想自定义内存中数据的数量,可以使用new SXSSFWorkBook。

@Test
public void testWrite07BigDataS() throws Exception{
    // 开始时间
    long start = System.currentTimeMillis();
    // 创建工作簿
    Workbook workbook = new SXSSFWorkbook();
    // 创建一张表
    Sheet sheet = workbook.createSheet();
    // 写入数据
    for (int rowNum = 0; rowNum < 10 * 10000; rowNum++) {
        Row row = sheet.createRow(rowNum);
        for (int cellNum = 0; cellNum < 10; cellNum++) {
            Cell cell = row.createCell(cellNum);
            cell.setCellValue(cellNum);
        }
    }
    FileOutputStream fileOutputStream = new FileOutputStream(System.getProperty("user.dir") + "/file/testWrite07BigDataS.xlsx");
    workbook.write(fileOutputStream);
    // 删除临时文件
    ((SXSSFWorkbook) workbook).dispose();
    fileOutputStream.close();
    long end = System.currentTimeMillis();
    System.out.println("耗时:" + (end - start) + "ms");
}

read

HSSF读取xls(2003版本)

@Test
public void testRead03() throws Exception {
    // 获取文件流
    FileInputStream inputStream = new FileInputStream(System.getProperty("user.dir") + "/file/统计表03.xls");
    // 创建一个工作簿
    Workbook workbook = new HSSFWorkbook(inputStream);
    // 获取表
    Sheet sheet = workbook.getSheetAt(0);
    // 获取行
    Row row = sheet.getRow(0);
    // 获取列
    Cell cell = row.getCell(0);
    System.out.println(cell.getStringCellValue());
    // 关闭流
    inputStream.close();
}

XSSF读取xlsx(2007版本)

@Test
public void testRead07() throws Exception {
    // 获取文件流
    FileInputStream inputStream = new FileInputStream(System.getProperty("user.dir") + "/file/统计表07.xlsx");
    // 创建一个工作簿
    Workbook workbook = new XSSFWorkbook(inputStream);
    // 获取表
    Sheet sheet = workbook.getSheetAt(0);
    // 获取行
    Row row = sheet.getRow(0);
    // 获取列
    Cell cell = row.getCell(0);
    System.out.println(cell.getStringCellValue());
    // 关闭流
    inputStream.close();
}

测试读取完整表

@Test
public void testReadCell() throws Exception {
    // 获取文件流
    FileInputStream inputStream = new FileInputStream(System.getProperty("user.dir") + "/file/学生花名册.xlsx");
    // 创建工作簿
    Workbook workbook = new XSSFWorkbook(inputStream);
    // 获取表
    Sheet sheet = workbook.getSheetAt(0);
    // 获取标题
    Row rowTitle = sheet.getRow(0);
    // 输出标题
    System.out.print("|  ");
    if (rowTitle != null) {
        int cellCount = rowTitle.getPhysicalNumberOfCells();
        for (int cellNum = 0; cellNum < cellCount; cellNum++) {
            Cell cell = rowTitle.getCell(cellNum);
            if (cell != null) {
                String cellValue = cell.getStringCellValue();
                System.out.print(cellValue + "  |  ");
            }
        }
    }
    System.out.println();
    // 获取行数
    int rowCount = sheet.getLastRowNum();
    // 获取数据
    for (int rowNum = 1; rowNum < rowCount; rowNum++) {
        Row rowData = sheet.getRow(rowNum);
        if (rowData != null) {
            int cellCount = rowData.getPhysicalNumberOfCells();
            for (int cellNum = 0; cellNum < cellCount; cellNum++) {
                Cell cell = rowData.getCell(cellNum);
                // 匹配单元格的数据类型
                if (cell != null) {
                    String cellValue = cell.getStringCellValue();
                    System.out.print(cellValue + "\t");
                }
            }
            System.out.println();
        }
    }
    inputStream.close();
}

EasyExcel

dependency

<!-- test -->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>
<!-- lombok -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.16</version>
</dependency>
<!-- easyexcel-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>2.2.7</version>
</dependency>
<!-- fastjson -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.75</version>
</dependency>
<!-- xls(2003) -->
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>3.17</version>
</dependency>
<!-- xlsx(2007) -->
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>3.17</version>
</dependency>

write

准备数据

@Data
@AllArgsConstructor
@NoArgsConstructor
public class DemoData implements Serializable, Cloneable {

    @ExcelProperty("字符串字段")
    private String stringData;

    @ExcelProperty("日期字段")
    private Date dateData;

    @ExcelProperty("整数字段")
    private Integer intData;

    @ExcelProperty("浮点字段")
    private Double douData;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

测试写入

private List<DemoData> data() {
    List<DemoData> list = new ArrayList<>();
    Random random = new Random();
    for (int i = 1; i <= 10; i++) {
        DemoData demoData = new DemoData();
        demoData.setStringData(i + "号");
        demoData.setDateData(new Date());
        demoData.setIntData(random.nextInt(100));
        demoData.setDouData(random.nextDouble());
    }
    return list;
}

@org.junit.Test
    public void write() {
    String fileName = System.getProperty("user.dir") + "/file/simpleWrite.xlsx";
    EasyExcel.write(fileName, DemoData.class).sheet("表1").doWrite(data());
}

read

数据解析器

public class DemoDataListener extends AnalysisEventListener<DemoData> {
    private static final int BATCH_COUNT = 5;
    private static final List<DemoData> list = new ArrayList<>();

    // 读取数据调用
    @Override
    public void invoke(DemoData demoData, AnalysisContext analysisContext) {
        System.out.println(demoData);
        if (list.size() == BATCH_COUNT) {
            list.clear();
        }
    }

    // 解析完成调用
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        System.out.println("解析完成");
    }
}

测试读取

@Test
public void read() {
    String fileName = System.getProperty("user.dir") + "/file/simpleWrite.xlsx";
    EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead();
}

总结

对于单纯的表格数据操作,POI的使用还是比较好的。
EasyExcel的封装性比较强,OOP和AOP很有针对性。
EasyExcel做一些数据库的批量导出导入还是不错的。

鉴于POI操作上的自由性,自己封装了一个工具类:

import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFFormulaEvaluator;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.junit.Test;

import java.io.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAccessor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;

/**
 * <p> Project: Excel </p>
 * <p> Package: top.parak.poi </p>
 * <p> FileName: ExcelUtil <p>
 * <p> Description: Excel工具类 <p>
 * <p> Created By IntelliJ IDEA </p>
 *
 * @author KHighness
 * @since 2021/5/19
 */
public class ExcelUtil {
    /** office2003 */
    private final static String EXCEL_TYPE_XLS = "xls";
    /** office2007 */
    private final static String EXCEL_TYPE_XLSX = "xlsx";
    /** 空字符串 */
    private final static String CHAR_TYPE_BLANK = "";
    /** 错误字符 */
    private final static String CHAR_TYPE_ERROR = "error";
    /** 未知字符 */
    private final static String CHAR_TYPE_UNKNOWN = "unknown";
    /** 列的长度单位 */
    private final static Integer COLUMN_CELL_WIDTH = 1 << 10;
    /** 时间字符串格式 */
    private final static String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss.SSS";
    /** 时间字符串格式化器 */
    private final static DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern(DATE_FORMAT);

    /**
     * 读取表格文件并解析成字符串数组
     * @param file 表格文件
     * @return 数据集合
     */
    public static List<List<Object>> readExcel(File file) {
        try {
            checkFile(file);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        Workbook workBook = getWorkBook(file);
        return readProcess(workBook);
    }

    /**
     * 检验文件是否存在,并且为表格文件
     * @param file 文件
     * @throws NullPointerException 未找到文件
     * @throws FileNotFoundException 非表格文件
     * @throws IllegalArgumentException 后缀名错误
     */
    private static void checkFile(File file) throws FileNotFoundException {
        if (file == null) {
            throw new NullPointerException("File is null");
        }
        if(!file.exists()){
            throw new FileNotFoundException("File " + file + " does not exist");
        }
        String fileName = file.getName();
        if(!fileName.endsWith(EXCEL_TYPE_XLS) && !fileName.endsWith(EXCEL_TYPE_XLSX)){
            throw new IllegalArgumentException(fileName + " is not a kind of excel file");
        }
    }

    /**
     * 获取文件对应的工作簿,分为XLS和XLSX类型
     * @param file 表格文件
     * @return 表格文件的工作簿
     */
    private static Workbook getWorkBook(File file) {
        String fileName = file.getName();
        Workbook workbook = null;
        try {
            InputStream in = new FileInputStream(file);
            workbook = fileName.endsWith(EXCEL_TYPE_XLS) ? new HSSFWorkbook(in) : new XSSFWorkbook(in);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return workbook;
    }

    /**
     * 读取工作簿,一行数据读作一个数组,所有行作为一个集合返回
     * @param workbook 工作簿
     * @return 数据集合
     */
    private static List<List<Object>> readProcess(Workbook workbook) {
        List<List<Object>> res = new ArrayList<>();
        FormulaEvaluator evaluator = (workbook instanceof HSSFWorkbook) ?
                new HSSFFormulaEvaluator((HSSFWorkbook) workbook) : new XSSFFormulaEvaluator((XSSFWorkbook) workbook);
        if(workbook != null) {
            for (int sheetNum = 0; sheetNum < workbook.getNumberOfSheets(); sheetNum++) {
                Sheet sheet = workbook.getSheetAt(sheetNum);
                if (sheet == null)
                    continue;
                int firstRowNum = sheet.getFirstRowNum();
                int lastRowNum = sheet.getLastRowNum();
                for (int rowNum = firstRowNum; rowNum <= lastRowNum; rowNum++) {
                    Row row = sheet.getRow(rowNum);
                    if (row == null)
                        continue;
                    int firstCellNum = row.getFirstCellNum();
                    int lastCellNum = row.getPhysicalNumberOfCells();
                    List<Object> curr = new ArrayList<>(lastCellNum - firstCellNum + 1);
                    for (int cellNum = firstCellNum; cellNum < lastCellNum; cellNum++) {
                        Cell cell = row.getCell(cellNum);
                        curr.add(getCellValue(cell, evaluator));
                    }
                    res.add(curr);
                }
            }
            try {
                workbook.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return res;
    }

    /**
     * 获取单元格的值
     * @param cell 单元格
     * @param evaluator 公式计算
     * @return 单元格的值
     */
    private static Object getCellValue(Cell cell, FormulaEvaluator evaluator){
        Object cellValue = "";
        if(cell == null){
            return cellValue;
        }
        // 判断数据的类型
        switch (cell.getCellType()){
            case Cell.CELL_TYPE_NUMERIC: // 数字类型
                cellValue = cell.getNumericCellValue(); break;
            case Cell.CELL_TYPE_STRING: // 字符串类型
                cellValue = cell.getStringCellValue(); break;
            case Cell.CELL_TYPE_BOOLEAN: // 布尔类型
                cellValue = cell.getBooleanCellValue(); break;
            case Cell.CELL_TYPE_FORMULA: // 公式
                cellValue = evaluator.evaluate(cell); break;
            case Cell.CELL_TYPE_BLANK: // 空值
                cellValue = CHAR_TYPE_BLANK; break;
            case Cell.CELL_TYPE_ERROR: // 错误
                cellValue = CHAR_TYPE_ERROR; break;
            default: cellValue = CHAR_TYPE_UNKNOWN; break;
        }
        return cellValue;
    }

    /**
     * 将标题和内容写入新文件
     * @param file        文件
     * @param sheetName   表格名
     * @param titleList   标题列
     * @param contentList 内容列
     */
    public static void writeExcel(File file, String sheetName, List<String> titleList, List<List<Object>> contentList) {
        String fileName = checkParam(file, sheetName, titleList, contentList);
        Workbook workbook = fileName.endsWith(EXCEL_TYPE_XLS) ? new HSSFWorkbook() : new XSSFWorkbook();
        Sheet sheet = workbook.createSheet(sheetName);
        initHead(workbook, sheet, titleList);
        writeContent(workbook, sheet, contentList);
        try {
            FileOutputStream out = new FileOutputStream(file);
            workbook.write(out);
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 检查参数是否合法,并返回文件名
     * @param file        文件
     * @param sheetName   表格名
     * @param titleList   标题列
     * @param contentList 内容列
     * @return 文件名
     */
    private static String checkParam(File file, String sheetName, List<String> titleList, List<List<Object>> contentList) {
        if (file == null)
            throw new NullPointerException("File is null");
        String fileName = file.getName();
        if (fileName.equals("") || !(fileName.endsWith(EXCEL_TYPE_XLS) || fileName.endsWith(EXCEL_TYPE_XLSX)))
            throw new IllegalArgumentException("File name is illegal");
        if (sheetName == null || sheetName.equals(""))
            throw new IllegalArgumentException("Sheet name is blank");
        if (titleList == null)
            throw new IllegalArgumentException("Title list is null");
        if (contentList == null)
            throw new IllegalArgumentException("Content list is null");
        return fileName;
    }

    /**
     * 初始化标题行
     * @param book      工作簿
     * @param sheet     当前表
     * @param titleList 标题列
     */
    private static void initHead(Workbook book, Sheet sheet, List<String> titleList) {
        // 标题样式
        CellStyle style = book.createCellStyle();
        // 居中对齐
        style.setAlignment(CellStyle.ALIGN_CENTER);
        style.setVerticalAlignment(CellStyle.VERTICAL_CENTER);
        // 背景填充
        style.setFillForegroundColor(IndexedColors.GREY_50_PERCENT.getIndex());
        style.setFillPattern(CellStyle.SOLID_FOREGROUND);
        // 字体样式
        Font font = book.createFont();
        font.setFontName("华文楷体");
        font.setFontHeightInPoints((short) 13);
        font.setBoldweight(Font.BOLDWEIGHT_BOLD);
        font.setColor(IndexedColors.WHITE.getIndex());
        style.setFont(font);
        // 创建第一行
        Row head = sheet.createRow(0);
        head.setHeightInPoints(20);
        // 列的宽度
        int columnWidth = 0;
        for (int i = 0; i < titleList.size(); i++) {
            Cell cell = head.createCell(i);
            cell.setCellStyle(style);
            String s = titleList.get(i);
            cell.setCellValue(s);
            columnWidth = s.length() * COLUMN_CELL_WIDTH;
            sheet.setColumnWidth(i, columnWidth);
        }
    }

    /**
     * 填充内容行
     * @param book        工作簿
     * @param sheet       当前表
     * @param contentList 内容列
     */
    private static void writeContent(Workbook book, Sheet sheet, List<List<Object>> contentList) {
        for (int i = 0; i < contentList.size(); i++) {
            Row curr = sheet.createRow(i + 1);
            List<Object> content = contentList.get(i);
            for (int c = 0; c < content.size(); c++) {
                writeProcess(book, curr, c, content.get(c));
            }
        }
    }

    /**
     * 将值填入单元格
     * @param book 工作簿
     * @param row 当前行
     * @param col 当前列
     * @param val 单元值
     */
    private static void writeProcess(Workbook book, Row row, int col, Object val) {
        // 内容样式
        CellStyle style = book.createCellStyle();
        style.setVerticalAlignment(CellStyle.VERTICAL_CENTER);
        style.setBorderRight(CellStyle.BORDER_THIN);
        style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
        style.setBorderLeft(CellStyle.BORDER_THIN);
        style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
        style.setBorderTop(CellStyle.BORDER_THIN);
        style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
        style.setBorderBottom(CellStyle.BORDER_THIN);
        style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
        // 居中对齐
        style.setAlignment(CellStyle.ALIGN_CENTER);
        // 字体样式
        Font font = book.createFont();
        font.setFontName("Arial");
        font.setFontHeightInPoints((short) 10);
        style.setFont(font);
        Cell cell = row.createCell(col);
        cell.setCellStyle(style);
        try {
            if (val == null) {
                cell.setCellValue(CHAR_TYPE_BLANK);
            } else {
                if (val instanceof String) {
                    cell.setCellValue((String) val);
                } else if (val instanceof Integer) {
                    cell.setCellValue((int) val);
                } else if (val instanceof Long) {
                    cell.setCellValue((Long) val);
                } else if (val instanceof Double) {
                    cell.setCellValue((Double) val);
                } else if (val instanceof Float) {
                    cell.setCellValue((Float) val);
                } else if (val instanceof Boolean) {
                    cell.setCellValue((Boolean) val);
                } else if (val instanceof TemporalAccessor) {
                    cell.setCellValue(DATE_TIME_FORMATTER.format((TemporalAccessor) val));
                } else {
                    cell.setCellValue(val.toString());
                }
            }
        } catch (Exception e) {
            cell.setCellValue(CHAR_TYPE_ERROR);
            e.printStackTrace();
        }
    }
}