# poi 的介绍和使用

# poi 不同版本的介绍

poi-tlpoipoi-ooxml 是 Apache POI 的不同子库,它们各自用于不同的功能和文件格式

  1. Apache POI( poi ):
    • 功能:提供对 Microsoft Office 97-2003 格式 (如 .xls ) 文件的读写支持
    • 文件格式:主要用于处理 .xls 文件,这些文件是基于 HSSF ( Horrible SpreadSheet Format ) 的
  2. Apache POI-OOXML( poi-ooxml )
    • 功能:提供对 Microsoft Office 2007 及以后版本的文件格式 (如 .xlsx ) 的读写支持
    • 文件格式:主要用于处理 .xlsx 文件,这些文件是基于 XSSF ( XML Spreadsheet Format ) 的
    • 依赖:这个库依赖于 poi 库,因为它需要处理 .xls 文件中的一些老旧格式
  3. poi-tl:
    • 功能:主要用于处理和生成 Microsoft Word 文件 ( .docx ),并且是 Apache POI 的扩展库,简化了模版文档的处理
    • 文件格式:主要用于处理 .docx 文件,可以通过模版生成文档,使得生成文档更加简洁和高效
    • 可查阅官方文档:https://deepoove.com/poi-tl/#_why_poi_tl

总结

  • poi 主要用于处理旧版 Excel 文件 ( .xls )
  • poi-ooxml 主要用于处理新版 Excel 文件 ( .xlsx )
  • poi-tl 主要用于处理 Word 文档 ( .docx ),特别是当你需要模版支持

# poi 的使用

# 创建 excel

  1. 创建 excel 文档

    // 创建 name.xlsx 文件
    Workbook workbook = new XSSFWorkbook();
    // 创建 name.xls 文件
    Workbook wb = new HSSFWorkbook();
  2. 创建 sheet 页

    // 创建安全表名称
    String safeName = WorkbookUtil.createSafeSheetName("[O'Brien's sales*?]");
    Sheet sheet = workbook.createSheet("sheet页名称");
  3. 对 sheet 页的某索引处创建行对象

    Row header = sheet.createRow(0);
  4. 在改行的某索引处创建单元格并设置名称

    header.createCell(0).setCellValue("名称");
  5. 获取某行的全部单元格数量

    int rowTotal = header.getPhysicalNumberOfCells()

# 创建特殊数据类型对象

  1. 创建带有富文本格式的字符串 (可以对其某个字体设置不同的样式)

    XSSFCreationHelper creationHelper = workbook.getCreationHelper();
    XSSFRow row = sheet.createRow(0);
    XSSFRichTextString hyperlinkString = creationHelper.createRichTextString("abcdefghijk");
    // 创建字体对象
    XSSFFont font = workbook.createFont();
    font.setBold(true); // 设置粗体
    font.setColor(IndexedColors.RED.getIndex()); // 设置颜色
    hyperlinkString.applyFont(0, 4, font);
    XSSFFont fontTwo = workbook.createFont();
    fontTwo.setItalic(true);
    fontTwo.setColor(IndexedColors.BLUE.getIndex());
    hyperlinkString.applyFont(5, 8, fontTwo);
    XSSFCell cell = row.createCell(0);
    cell.setCellValue("click to url"); // 如果单元格的值 都设置在同一个的话会被 最后一个覆盖
    cell.setCellValue(hyperlinkString);

    效果:

    image-20240810172109727

  2. 创建超链接

    XSSFCreationHelper creationHelper = workbook.getCreationHelper();
    XSSFRow row = sheet.createRow(0);
    XSSFHyperlink hyperlinkURL = creationHelper.createHyperlink(HyperlinkType.URL);
    hyperlinkURL.setAddress("http://www.baidu.com"); // 设置跳转的路径
    XSSFCell cell = row.createCell(0);
    cell.setCellValue("click to url");  // 将单元格的值 和 超链接添加到 当前行中
    cell.setHyperlink(hyperlinkURL);

    效果:
    PixPin_2024-08-10_17-25-35

  3. 创建时间类型

    XSSFCreationHelper creationHelper = workbook.getCreationHelper();
    XSSFRow row = sheet.createRow(0);
    XSSFCellStyle style = workbook.createCellStyle(); // 创建样式对象
    style.setDataFormat(creationHelper.createDataFormat().getFormat("m/d/y h:mm")); // 格式化式样样式
    XSSFCell cell = row.createCell(0);  // 添加时间值
    cell.setCellValue(new Date());
    cell.setCellStyle(style);
    XSSFCell cellTwo = row.createCell(2);
    cellTwo.setCellValue(Calendar.getInstance());
    cellTwo.setCellStyle(style);

    效果:

    image-20240810173752112

  4. 创建多种格式的单元格

    XSSFWorkbook workbook = new XSSFWorkbook();
    String safeSheetName = WorkbookUtil.createSafeSheetName("[O'Brien's sales*?]");
    XSSFSheet sheet = workbook.createSheet(safeSheetName);
    XSSFRow row = sheet.createRow(0);
    // 类型:数字 (double)
    row.createCell(0).setCellValue(1.1);
    // 类型:日期 (Date)
    row.createCell(1).setCellValue(new Date());
    // 类型:日期 (Calendar)
    row.createCell(2).setCellValue(Calendar.getInstance());
    // 类型:字符串 (string)
    row.createCell(3).setCellValue("a string");
    // 类型:布尔 (boolean)
    row.createCell(4).setCellValue(true);
    // 类型:错误 (error)
    row.createCell(5).setCellType(CellType.ERROR);

    效果:

    image-20240810174113454

# 设置样式

  1. 通过 excel 文档对象创建样式对象

    CellStyle cellStyle = workbook.createCellStyle();
  2. 设置字体样式

    Font font = workbook.createFont();                        // 创建字体对象
    font.setBold(true);                                       // 加粗
    font.setFontHeightInPoints((short) 12);                   // 设置字体大小
    font.setFontName("微软雅黑");                              // 设置字体
    cellStyle.setFont(font);                                  // 向样式里添加字体设置

    # Files 和 InputStream (excel 文件读取操作)

    当打开 WorkBook (.xls HSSFWorkbook 或.xlsx XSSFWorkbook) 时,可以从 File 或 InputStream 加载工作簿

    使用 File 对象可以减少内存消耗,而 InputStream 需要更多内存,因为它必须缓冲整个文件

    如果使用 WorkbookFactory,则使用其中一个非常容易:

    // Use a file
    Workbook wb = WorkbookFactory.create(new File("MyExcel.xls"));
    // Use an InputStream, needs more memory
    Workbook wb = WorkbookFactory.create(new FileInputStream("MyExcel.xlsx"));

    代码案例:

    try (Workbook workbook = WorkbookFactory.create(new File("C:\\Users\\dkx\\Downloads\\test.xlsx"));
         Workbook workbookTwo = WorkbookFactory.create(Files.newInputStream(Paths.get("C:\\Users\\dkx\\Downloads\\test.xlsx")))) {
        // 获取第一个工作表
        Sheet sheet = workbook.getSheetAt(0);
        // 读取第 1 行,第 1 个单元格的内容
        Row row = sheet.getRow(0);
        Cell cell = row.getCell(0);
        String stringCellValue = cell.getStringCellValue();
        Cell cellTwo = row.getCell(1);
        double numericCellValue = cellTwo.getNumericCellValue();
        Cell cellThree = row.getCell(2);
        boolean booleanCellValue = cellThree.getBooleanCellValue();
        System.out.println("第1行,第1个单元格的内容是 = " + stringCellValue);
        System.out.println("第1行,第1个单元格的内容是 = " + numericCellValue);
        System.out.println("第1行,第1个单元格的内容是 = " + booleanCellValue);
        // 可以获取到 Content Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml
        System.out.println("workbook=" + workbook);
        // 可以获取到 Content Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml
        System.out.println("workbookTwo=" + workbookTwo);
    } catch (IOException e) {
        throw new RuntimeException(e);
    }

    效果:

    下面是我们要读取的 excel 表格内容

    image-20240810203902511

    读取的结果:

    第1行,第1个单元格的内容是 = a string text
    第1行,第1个单元格的内容是 = 1.1
    第1行,第1个单元格的内容是 = true
    workbook=Name: /xl/workbook.xml - Content Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml
    workbookTwo=Name: /xl/workbook.xml - Content Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml
    

    如果直接使用 HSSFWorkbook 或 XSSFWorkbook,通常应遍历 POIFSileSystem 或 OPCPackage,以完全控制生命周期 (包括完成后关闭文件)

    try {
        // HSSFWorkbook, File
        POIFSFileSystem fs = new POIFSFileSystem(new File("C:\\Users\\dkx\\Downloads\\test.xls"));
        HSSFWorkbook wb = new HSSFWorkbook(fs.getRoot(), true);
        System.out.println("wb = " + wb);
        fs.close();
        // HSSFWorkbook, InputStream, needs more memory
        POIFSFileSystem fs1 = new POIFSFileSystem(new File("C:\\Users\\dkx\\Downloads\\test.xls"));
        HSSFWorkbook wb1 = new HSSFWorkbook(fs.getRoot(), true);
        fs1.close();
        // XSSFWorkbook, File
        OPCPackage pkg = OPCPackage.open(new File("C:\\Users\\dkx\\Downloads\\test.xlsx"));
        XSSFWorkbook wb2 = new XSSFWorkbook(pkg);
        for (int i = 0; i < wb2.getNumberOfSheets(); i++) {
            XSSFSheet sheet = wb2.getSheetAt(i);
            System.out.println("sheetName = " + sheet);
            for (Row row : sheet) {
                for (Cell cell : row) {
                       switch(cell.getCellType()) {
                           case STRING:
                               System.out.println("string = " + cell.getStringCellValue());
                               break;
                           case NUMERIC:
                               System.out.println("numeric = " + cell.getNumericCellValue());
                               break;
                           case  BOOLEAN:
                               System.out.println("boolean = " + cell.getBooleanCellValue());
                               break;
                           case FORMULA:
                               System.out.println("formula = " + cell.getCellFormula());
                               break;
                           default:
                               System.out.println("没有任何数据");
                       }
                }
            }
        }
        pkg.close();
        // XSSFWorkbook, InputStream, needs more memory
        OPCPackage pkg1 = OPCPackage.open(new File("C:\\Users\\dkx\\Downloads\\test.xlsx"));
        XSSFWorkbook wb3 = new XSSFWorkbook(pkg);
        pkg1.close();
    } catch (Exception e) {
        throw new RuntimeException(e);
    }

    我们还是读取上次读取的 excel 文件内容结果如下:

    sheetName = Name: /xl/worksheets/sheet1.xml - Content Type: application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml
    string = a string text
    numeric = 1.1
    boolean = true
    

    # 不同的对齐方式

    public static void padding(HttpServletResponse response) {
            try(
                    Workbook wb = new XSSFWorkbook()) //or new HSSFWorkbook();
            {
                Sheet sheet = wb.createSheet();
                Row row = sheet.createRow(2);
                // 设置行的高度为 30 榜
                row.setHeightInPoints(30);
                // 设置字体  左右 (居中) 上下 (底部)
                createCell(wb, row, 0, HorizontalAlignment.CENTER, VerticalAlignment.BOTTOM);
                // 设置字体 左右 (中心选定对齐)  上下 (底部)
                createCell(wb, row, 1, HorizontalAlignment.CENTER_SELECTION, VerticalAlignment.BOTTOM);
                // 设置字体 左右 (填充对齐)  上下 (居中)
                createCell(wb, row, 2, HorizontalAlignment.FILL, VerticalAlignment.CENTER);
                // 设置字体  左右 (默认对齐)  上下 (居中)
                createCell(wb, row, 3, HorizontalAlignment.GENERAL, VerticalAlignment.CENTER);
                // 设置字体  左右 (拉伸对齐)  上下 (拉伸对齐)
                createCell(wb, row, 4, HorizontalAlignment.JUSTIFY, VerticalAlignment.JUSTIFY);
                // 设置字体  左右 (左对齐)  上下 (顶部)
                createCell(wb, row, 5, HorizontalAlignment.LEFT, VerticalAlignment.TOP);
                // 设置字体  左右 (右对齐)  上下 (顶部)
                createCell(wb, row, 6, HorizontalAlignment.RIGHT, VerticalAlignment.TOP);
                response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
                response.setHeader("Content-Disposition", "attachment;filename=test1.xlsx");
                wb.write(response.getOutputStream());
                response.getOutputStream().flush();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
    }
    private static void createCell(Workbook wb, Row row, int column, HorizontalAlignment halign, VerticalAlignment valign) {
        Cell cell = row.createCell(column);
        cell.setCellValue("Align It");
        CellStyle cellStyle = wb.createCellStyle();
        // 设置左右对齐方式
        cellStyle.setAlignment(halign);
        // 设置上下对齐方式
        cellStyle.setVerticalAlignment(valign);
        cell.setCellStyle(cellStyle);
    }

    效果:

    image-20240810210433997

    # 设置边框

    public static void border(HttpServletResponse response) {
        try(Workbook wb = new HSSFWorkbook()) {
            Sheet sheet = wb.createSheet("new sheet");
            // Create a row and put some cells in it. Rows are 0 based.
            Row row = sheet.createRow(1);
            // Create a cell and put a value in it.
            Cell cell = row.createCell(1);
            cell.setCellValue(4);
            // Style the cell with borders all around.
            CellStyle style = wb.createCellStyle();
            // 设置底部的线 和 样式 为 细线
            style.setBorderBottom(BorderStyle.THIN);
            // 设置底部的线的颜色为蓝色
            style.setBottomBorderColor(IndexedColors.BLACK.getIndex());
            // 设置左侧的线 和 样式 为 细线
            style.setBorderLeft(BorderStyle.THIN);
            // 设置左侧的线的颜色为绿色
            style.setLeftBorderColor(IndexedColors.GREEN.getIndex());
            // 设置右侧的线 和 样式为 细线
            style.setBorderRight(BorderStyle.THIN);
            // 设置右侧的线的颜色为 蓝色
            style.setRightBorderColor(IndexedColors.BLUE.getIndex());
            // 设置顶部的线 和 样式为 中等粗细的虚线边框
            style.setBorderTop(BorderStyle.MEDIUM_DASHED);
            // 设置顶部的线的颜色为黑色
            style.setTopBorderColor(IndexedColors.BLACK.getIndex());
            // 应用到 单元格中
            cell.setCellStyle(style);
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setHeader("Content-Disposition", "attachment;filename=test2.xlsx");
            wb.write(response.getOutputStream());
            response.getOutputStream().flush();
        } catch(Exception e) {
            throw new RuntimeException(e);
        }
    }

    效果:

    image-20240810211419981

    # 遍历行和单元格

    有时,可能只想遍历工作簿中的所有工作表中的所有行或行中的所有单元格。这可以通过简单的 for 循环来实现。

    通过调用 workbook.sheetIterator (),sheet.rowIterator () 和 row.cellIterator () 或隐式使用 for-each 循环,可以使用这些迭代器。请注意,rowIterator 和 cellIterator 遍历已创建的行或单元格,会跳过空的行和单元格

    for (Sheet sheet : wb ) {
        for (Row row : sheet) {
            for (Cell cell : row) {
                // Do something here
            }
        }
    }

    # 遍历单元格,控制丢失 / 空白的单元格

    在某些情况下,进行迭代时,您需要完全控制如何处理丢失或空白的行和单元格,并且需要确保访问每个单元格,而不仅仅是访问文件中定义的那些单元格。(CellIterator 将仅返回文件中定义的单元格,这在很大程度上是具有值或样式的单元格,但取决于 Excel)

    在这种情况下,应获取一行的第一列和最后一列信息,然后调用 getCell (int ,MissingCellPolicy) 来获取单元格。使用 MissingCellPolicy 控制空白或单元格的处理方式

    MissingCellPolicy 策略如下:

    Row.MissingCellPolicy 是一个枚举,定义了处理缺失单元格的不同策略。常用的枚举值包括:

    1. RETURN_NULL_AND_BLANK :
      • 返回的单元格可能是 null ,也可能是 BLANK 类型。 null 表示单元格不存在,而 BLANK 表示单元格存在但为空白。
    2. CREATE_NULL_AS_BLANK :
      • 如果单元格不存在,会创建一个 BLANK 类型的单元格并返回。
    3. RETURN_BLANK_AS_NULL :
      • 如果单元格不存在或为空白(即 BLANK 类型),则返回 null
    4. RETURN_MISSING_AS_BLANK :
      • 如果单元格不存在,会返回一个 BLANK 类型的单元格,但不会创建它。
    private static final int MY_MINIMUM_COLUMN_COUNT = 5
    public void processExcel(HttpServletResponse response) {
        try (Workbook workbook = WorkbookFactory.create(Files.newInputStream(Paths.get("C:\\Users\\dkx\\Downloads\\test2.xlsx")))) {
            Sheet sheet = workbook.getSheetAt(0);
            int rowStart = Math.min(15, sheet.getFirstRowNum());
            int rowEnd = Math.max(1400, sheet.getLastRowNum());
            for (int rowNum = rowStart; rowNum < rowEnd; rowNum++) {
                Row r = sheet.getRow(rowNum);
                if (r == null) {
                    // 处理空行,例如记录日志或跳过
                    System.out.println(rowNum + " 处为空行 = " + r);
                    continue;
                }
                int lastColumn = Math.max(r.getLastCellNum(), MY_MINIMUM_COLUMN_COUNT);
                for (int cn = 0; cn < lastColumn; cn++) {
                // 如果单元格不存在则返回 从枚举中获取的策略如果不存在不是返回空白 (即 BLANK 类型) 而是返回 NULL
                    Cell c = r.getCell(cn, Row.MissingCellPolicy.RETURN_BLANK_AS_NULL);
                    if (c == null) {
                        // 处理空单元格,例如填充默认值或跳过
                        Cell cell = r.createCell(cn);
                        cell.setCellValue("ohhhhhhhhhhhh");
                    } else {
                        // 对单元格内容进行操作,例如打印内容或修改数据
                        System.out.println("Cell Value: " + Convert.toStr(c));
                    }
                }
            }
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setHeader("Content-Disposition", "attachment;filename=test3.xlsx");
            workbook.write(response.getOutputStream());
            response.getOutputStream().flush();
        } catch(Exception e) {
            throw new RuntimeException(e);
        }
    }

    效果:

    image-20240811103737832

    image-20240811103835302

    # 获取单元格内容

    要获取单元格的内容,您首先需要知道它是哪种单元格 (例如:将字符串单元格作为其数字内容将获得 NumberFormatException)。因此,您将需要打开单元格的类型,然后为该单元格调用适当的 getter

    在下面代码中,我们遍历一张纸中的每个单元格,打印出该单元格的引用 (例如 A3),然后打印出该单元格的内容

    public static void CellContents() {
        try(Workbook wb = WorkbookFactory.create(Files.newInputStream(Paths.get("C:\\Users\\dkx\\Downloads\\test.xlsx")))) {
            // import org.apache.poi.ss.usermodel.*;
            DataFormatter formatter = new DataFormatter();
            Sheet sheet1 = wb.getSheetAt(0);
            for (Row row : sheet1) {
                for (Cell cell : row) {
                    // 根据 行 列 坐标获取当前 单元格的位置 比如 A1, B1, A2, B2 等
                    CellReference cellRef = new CellReference(row.getRowNum(), cell.getColumnIndex());
                    System.out.print(cellRef.formatAsString());
                    System.out.print(" - ");
                    // get the text that appears in the cell by getting the cell value and applying any data formats (Date, 0.00, 1.23e9, $1.23, etc)
                    String text = formatter.formatCellValue(cell);
                    System.out.println(text);
                    // Alternatively, get the value and format it yourself
                    switch (cell.getCellType()) {
                        case STRING:
                            System.out.println(cell.getRichStringCellValue().getString());
                            break;
                        case NUMERIC:
                            if (DateUtil.isCellDateFormatted(cell)) {
                                System.out.println(cell.getDateCellValue());
                            } else {
                                System.out.println(cell.getNumericCellValue());
                            }
                            break;
                        case BOOLEAN:
                            System.out.println(cell.getBooleanCellValue());
                            break;
                        case FORMULA:
                            System.out.println(cell.getCellFormula());
                            break;
                        case BLANK:
                            System.out.println();
                            break;
                        default:
                            System.out.println();
                    }
                }
            }
        } catch(Exception e) {
            throw new RuntimeException(e);
        }
    }

    效果:

    image-20240811110405815

    image-20240811110416854

    # 文字提取

    对于大多数文本提取要求,标准 ExcelExtractor 类应提供您所需要的全部

    # 介绍:

    ExcelExtractor:是用来从 Excel 文件中提取文本内容的。以下是这两行代码的详细解释

    setFormulasNotResults:该设置决定了在提取文本时,Excel 文件中的公式应该被提取为公式本身,还是被提取为公式计算的结果

    • true : 提取公式本身。例如,如果单元格包含公式 =SUM(A1:B1) ,提取的内容将是 =SUM(A1:B1)

    • false : 提取公式计算的结果。例如,如果单元格包含公式 =SUM(A1:B1) ,并且计算结果为 10 ,提取的内容将是 10

    setIncludeSheetNames:该设置决定了在提取文本时,是否包括工作表的名称

    • true :在提取的文本中包括工作表的名称
    • false :不包括工作表的名称,提取的文本将只包含单元格内容

    提取 xls 文件代码案例如下:

    public static void HSSExtractTheText() {
        try(
                POIFSFileSystem poifsFileSystem = new POIFSFileSystem(new File("C:\\Users\\dkx\\Downloads\\testx.xls"));
                HSSFWorkbook workbook = new HSSFWorkbook(poifsFileSystem);
        ) {
            ExcelExtractor excelExtractor = new ExcelExtractor(workbook);
            excelExtractor.setFormulasNotResults(true);
            excelExtractor.setIncludeSheetNames(false);
            String text = excelExtractor.getText();
            System.out.println("HSSF content = " + text);
        } catch(Exception e) {
            throw new RuntimeException(e);
        }
    }

    效果:

    image-20240811145254670

    提取 xlsx 文件代码案例如下:

    public static void XSSExtractTheText() {
        try {
            OPCPackage open = OPCPackage.open(new File("C:\\Users\\dkx\\Downloads\\test.xlsx"));
            try (XSSFWorkbook wb = new XSSFWorkbook(open)) {
                XSSFExcelExtractor extractor = new XSSFExcelExtractor(wb);
                extractor.setFormulasNotResults(true);
                extractor.setIncludeSheetNames(false);
                String text = extractor.getText();
                System.out.println("XSSF content = " + text);
            }
        } catch(Exception e) {
            throw new RuntimeException(e);
        }
    }

    效果:

    image-20240811145827037

    对于非常精美的文本提取,XLS 到 CSV 等,请查看

    /src/examples/src/org/apache/poi/examples/hssf/eventusermodel/XLS2CSVmra.java

    # 填充和颜色

    public static void paddingTwo(HttpServletResponse response) {
        try(Workbook wb = new XSSFWorkbook()) {
            Sheet sheet = wb.createSheet("new sheet");
            // Create a row and put some cells in it. Rows are 0 based.
            Row row = sheet.createRow(1);
            // Aqua background
            CellStyle style = wb.createCellStyle();
            style.setFillBackgroundColor(IndexedColors.AQUA.getIndex());
            style.setFillPattern(FillPatternType.BIG_SPOTS);
            Cell cell = row.createCell(1);
            cell.setCellValue("X");
            cell.setCellStyle(style);
            // Orange "foreground", foreground being the fill foreground not the font color.
            style = wb.createCellStyle();
            style.setFillForegroundColor(IndexedColors.ORANGE.getIndex());
            style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
            cell = row.createCell(2);
            cell.setCellValue("X");
            cell.setCellStyle(style);
            // Write the output to a file
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setHeader("Content-disposition", "attachment;filename=test123.xlsx");
            wb.write(response.getOutputStream());
            response.getOutputStream().flush();
        } catch(Exception e) {
            throw new RuntimeException(e);
        }
    }

    效果:

    image-20240811151115986

    # 合并单元格

    public static void merge(HttpServletResponse response) {
        try(Workbook wb = new HSSFWorkbook()) {
            Sheet sheet = wb.createSheet("new sheet");
            Row row = sheet.createRow(1);
            Cell cell = row.createCell(1);
            cell.setCellValue("This is a test of merging");
            // 通过坐标位置来进行区域的单元格合并从起始 到 结束 进行合并 不能将坐标都设置一样,因为这样不能合并会报错
            sheet.addMergedRegion(new CellRangeAddress(
                    1, //first row (0-based)
                    3, //last row  (0-based)
                    1, //first column (0-based)
                    4  //last column  (0-based)
            ));
            // Write the output to a file
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setHeader("Content-Disposition", "attachment;filename=test321.xlsx");
            wb.write(response.getOutputStream());
            response.getOutputStream().flush();
        } catch(Exception e) {
            throw new RuntimeException(e);
        }
    }

    效果:

    image-20240811152253234

    # 设置字体

    public static void setFont(HttpServletResponse response) {
        try(Workbook wb = new HSSFWorkbook()) {
            Sheet sheet = wb.createSheet("new sheet");
            // Create a row and put some cells in it. Rows are 0 based.
            Row row = sheet.createRow(1);
            // Create a new font and alter it.
            Font font = wb.createFont();
            // 字体大小
            font.setFontHeightInPoints((short)24);
            // 设置字体
            font.setFontName("微软雅黑");
            font.setItalic(true);
            // 删除线
            font.setStrikeout(true);
            // 加粗
            font.setBold(true);
            // 颜色
            font.setColor(HSSFColor.HSSFColorPredefined.RED.getIndex());
            // Fonts are set into a style so create a new one to use.
            CellStyle style = wb.createCellStyle();
            // 向样式里添加字体设置
            style.setFont(font);
            // Create a cell and put a value in it.
            Cell cell = row.createCell(1);
            cell.setCellValue("This is a test of fonts");
            cell.setCellStyle(style);
            // Write the output to a file
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setHeader("Content-Disposition", "attachment;filename=ttt.xlsx");
            wb.write(response.getOutputStream());
            response.getOutputStream().flush();
        } catch(Exception e) {
            throw new RuntimeException(e);
        }
    }

    效果:

    image-20240811153535715

    请注意,工作簿中唯一字体的最大数限制为 32767。您应该在应用程序中重新使用字体,而不是为每个单元格创建字体

    例子:

    错误的写法:

    for (int i = 0; i < 10000; i++) {
        Row row = sheet.createRow(i);
        Cell cell = row.createCell(0);
        CellStyle style = workbook.createCellStyle();
        Font font = workbook.createFont();
        font.setBoldweight(Font.BOLDWEIGHT_BOLD);
        style.setFont(font);
        cell.setCellStyle(style);
    }

    正确的写法:

    CellStyle style = workbook.createCellStyle();
    Font font = workbook.createFont();
    font.setBoldweight(Font.BOLDWEIGHT_BOLD);
    style.setFont(font);
    for (int i = 0; i < 10000; i++) {
        Row row = sheet.createRow(i);
        Cell cell = row.createCell(0);
        cell.setCellStyle(style);
    }

    # 自定义颜色

    HSSF (.xls 文件)

    public static void diyColor(HttpServletResponse response) {
        try(HSSFWorkbook wb = new HSSFWorkbook()) {
            HSSFSheet sheet = wb.createSheet();
            HSSFRow row = sheet.createRow(0);
            HSSFCell cell = row.createCell(0);
            HSSFPalette palette = wb.getCustomPalette();
            // 通过 Excel 中的 调色板 来设置 指定 RED 颜色 的 RGB 方案
            palette.setColorAtIndex(HSSFColor.HSSFColorPredefined.RED.getIndex(),
                    (byte) 153,  //RGB red (0-255)
                    (byte) 0,    //RGB green
                    (byte) 0     //RGB blue
            );
            // 通过 Excel 中的 调色板 来设置 指定 LIME 颜色 的 RGB 方案
            palette.setColorAtIndex(HSSFColor.HSSFColorPredefined.LIME.getIndex(), (byte) 255, (byte) 204, (byte) 102);
            // 将 调整的 指定颜色设置到字体中
            HSSFFont font = wb.createFont();
            font.setColor(HSSFColor.HSSFColorPredefined.RED.getIndex());
            HSSFCellStyle style = wb.createCellStyle();
            // 设置填充色前景
            style.setFillForegroundColor(HSSFColor.HSSFColorPredefined.LIME.getIndex());
            // 设置填充方案为实心前景
            style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
            style.setFont(font);
            cell.setCellStyle(style);
            cell.setCellValue("Modified Palette");
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setHeader("Content-Disposition", "attachment;filename=qwe.xls");
            wb.write(response.getOutputStream());
            response.getOutputStream().flush();
        } catch(Exception e) {
            throw new RuntimeException(e);
        }
    }

    效果:

    image-20240811183303747

    XSSF (.xlsx 文件)

    public static void diyColorX(HttpServletResponse response) {
        try(XSSFWorkbook wb = new XSSFWorkbook()) {
            XSSFSheet sheet = wb.createSheet();
            XSSFRow row = sheet.createRow(0);
            XSSFCell cell = row.createCell( 0);
            XSSFCellStyle style = wb.createCellStyle();
            // 设置填充前景颜色
            style.setFillForegroundColor(new XSSFColor(new java.awt.Color(128, 0, 128), new DefaultIndexedColorMap()));
            // 设置填充方案为实心前景
            style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
            cell.setCellStyle(style);
            cell.setCellValue("custom XSSF colors");
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setHeader("Content-Disposition", "attachment;filename=qwe.xlsx");
            wb.write(response.getOutputStream());
            response.getOutputStream().flush();
        } catch(Exception e) {
            throw new RuntimeException(e);
        }
    }

    效果:

    image-20240811184204629

    # 读和写 WorkBooks

    try (InputStream inp = new FileInputStream("workbook.xls")) {
    //InputStream inp = new FileInputStream("workbook.xlsx");
        Workbook wb = WorkbookFactory.create(inp);
        Sheet sheet = wb.getSheetAt(0);
        Row row = sheet.getRow(2);
        Cell cell = row.getCell(3);
        if (cell == null)
            cell = row.createCell(3);
        cell.setCellType(CellType.STRING);
        cell.setCellValue("a test");
        // Write the output to a file
        try (OutputStream fileOut = new FileOutputStream("workbook.xls")) {
            wb.write(fileOut);
        }
    }

    # 在单元格中使用换行符

    public static void lineBreaks(HttpServletResponse response) {
        try(Workbook wb = new XSSFWorkbook())    //or new HSSFWorkbook();
        {
            Sheet sheet = wb.createSheet();
            Row row = sheet.createRow(2);
            Cell cell = row.createCell(2);
            cell.setCellValue("Use \n with word wrap on to create a new line");
            //to enable newlines you need set a cell styles with wrap=true
            CellStyle cs = wb.createCellStyle();
            // 运行换行展示
            cs.setWrapText(true);
            cell.setCellStyle(cs);
            // 设置行高
            row.setHeightInPoints((2 * sheet.getDefaultRowHeightInPoints()));
            // 自动调整列宽,从 索引 2 开始
            sheet.autoSizeColumn(2);
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setHeader("Content-Disposition", "attachment;filename=qwe.xlsx");
            wb.write(response.getOutputStream());
            response.getOutputStream().flush();
        } catch(Exception e) {
            throw new RuntimeException(e);
        }
    }

    效果:

    image-20240811190225813

    # 数据格式化

    public static void dataFormat(HttpServletResponse response) {
        try(Workbook wb = new HSSFWorkbook()) {
            Sheet sheet = wb.createSheet("format sheet");
            CellStyle style;
            DataFormat format = wb.createDataFormat();
            Row row;
            Cell cell;
            int rowNum = 0;
            int colNum = 0;
            row = sheet.createRow(rowNum++);
            cell = row.createCell(colNum);
            cell.setCellValue(11111.25);
            style = wb.createCellStyle();
            // 保留一位小数
            style.setDataFormat(format.getFormat("0.0"));
            cell.setCellStyle(style);
            row = sheet.createRow(rowNum++);
            cell = row.createCell(colNum);
            cell.setCellValue(11111.25);
            style = wb.createCellStyle();
            // #,## 数字部分以,分隔。保留四位小数
            style.setDataFormat(format.getFormat("#,##0.0000"));
            cell.setCellStyle(style);
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setHeader("Content-Disposition", "attachment;filename=qwe.xls");
            wb.write(response.getOutputStream());
            response.getOutputStream().flush();
        } catch(Exception e) {
            throw new RuntimeException(e);
        }
    }

    效果:

    image-20240811190840875

    # 将工作表调整为一页 (打印相关配置)

    try (Workbook wb = new HSSFWorkbook()) {
        // 创建一个新的 Excel 工作簿(.xls 格式)
        Sheet sheet = wb.createSheet("format sheet");
        // 获取当前工作表的打印设置
        PrintSetup ps = sheet.getPrintSetup();
        
        // 自动分页
        sheet.setAutobreaks(true);
        // 设置打印页面的高度和宽度为 1 页
        ps.setFitHeight((short)1);
        ps.setFitWidth((short)1);
        // 设置 HTTP 响应的内容类型为 Excel 2003 格式
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        
        // 设置文件下载时的名称
        response.setHeader("Content-Disposition", "attachment;filename=qwe.xls");
        // 将工作簿写入 HTTP 响应输出流
        wb.write(response.getOutputStream());
    } catch (Exception e) {
        throw new RuntimeException(e);
    }

    # 设置打印区域

    Workbook wb = new HSSFWorkbook();
    Sheet sheet = wb.createSheet("Sheet1");
    //sets the print area for the first sheet
    wb.setPrintArea(0, "$A$1:$C$2");
    //Alternatively:
    wb.setPrintArea(
            0, //sheet index
            0, //start column
            1, //end column
            0, //start row
            0  //end row
    );
    try (OutputStream fileOut = new FileOutputStream("workbook.xls")) {
        wb.write(fileOut);
    }
    wb.close();

    # 设置页脚页数

    Workbook wb = new HSSFWorkbook(); // or new XSSFWorkbook();
    Sheet sheet = wb.createSheet("format sheet");
    Footer footer = sheet.getFooter();
    footer.setRight( "Page " + HeaderFooter.page() + " of " + HeaderFooter.numPages() );
    // Create various cells and rows for spreadsheet.
    try (OutputStream fileOut = new FileOutputStream("workbook.xls")) {
        wb.write(fileOut);
    }
    wb.close();

# poi 导出 excel 的基本使用

使用步骤:

  1. 查询需要导出的数据
  2. 创建 excel 文档 (new XSSFWorkbook () 返回 Workbook 对象)
  3. 创建存放数据的 sheet 页 (workbook.createSheet ("管段信息") 返回 Sheet 对象)
  4. 创建行对象,提供创建这个行所处的索引位置 (sheet.createRow (0) 返回 Row 对象)
  5. 对行进行添加标题数据 (row.createCell (0).setCellValue ("管线名称") 创建单元格 设置 row 行的索引为 0 处的数据)
  6. 对行进行添加实际数据 (可以遍历对象来通过循环值当作创建行的索引位置 然后对这个行进行添加数据)
  7. 对标题进行设置样式 (通过 workbook 对象创建 workbook.createCellStyle () 返回 CellStyle 对象)
  8. 对字体进行设置
  9. 遍历某行的所有单元格 (header.getPhysicalNumberOfCells () for 循环) 并将字体的设置应用上 (row.getCell (i).setCellStyle (cellStyle))

代码如下:

public void exportSection(String layoutId, HttpServletResponse response) {
    // 查询数据
    List<FacGasPipeSection> facGasPipeSections = facGasPipeSectionMapper.selectList(null);
    List<FacGasStationYard> facGasStationYards = facGasStationYardMapper.selectList(null);
        // 创建 Excel 文档
        try(Workbook workbook = new XSSFWorkbook()) {
        // 创建两个 Sheet 分别存放不同的数据
        createPipeSectionSheet(workbook, facGasPipeSections);
        createStationYardSheet(workbook, facGasStationYards);
        // 设置 Content-Type 为 Excel
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setHeader("Content-Disposition", "attachment;filename=export.xlsx");
        // 将 Excel 写入响应输出流
        workbook.write(response.getOutputStream());
        response.getOutputStream().flush();
    } catch(Exception e) {
        throw new RuntimeException(e);
    }
}
private void createPipeSectionSheet(Workbook workbook, List<FacGasPipeSection> facGasPipeSections) {
    Sheet sheet = workbook.createSheet("管段信息");
    Row header = sheet.createRow(0);
    // 创建标题行
    header.createCell(0).setCellValue("管线名称");
    header.createCell(1).setCellValue("管线编码");
    header.createCell(2).setCellValue("管段名称");
    header.createCell(3).setCellValue("管段编码");
    header.createCell(4).setCellValue("管段长度");
    header.createCell(5).setCellValue("建设状态");
    fontStyle(workbook, header);
    // 填充数据
    for (int i = 0; i < facGasPipeSections.size(); i++) {
        Row row = sheet.createRow(i + 1);
        FacGasPipeSection section = facGasPipeSections.get(i);
        row.createCell(0).setCellValue(Optional.ofNullable(section).map(FacGasPipeSection::getPipelineName).orElse(""));
        row.createCell(1).setCellValue(Optional.ofNullable(section).map(FacGasPipeSection::getPipelineCode).orElse(""));
        row.createCell(2).setCellValue(Optional.ofNullable(section).map(FacGasPipeSection::getPipeSectionName).orElse(""));
        row.createCell(3).setCellValue(Optional.ofNullable(section).map(FacGasPipeSection::getPipeSectionCode).orElse(""));
        row.createCell(4).setCellValue(Optional.ofNullable(section).map(FacGasPipeSection::getPipeSectionLength).orElse(0D));
        row.createCell(5).setCellValue(Optional.ofNullable(section).map(FacGasPipeSection::getBuildStatus).orElse(""));
    }
}
private void createStationYardSheet(Workbook workbook, List<FacGasStationYard> facGasStationYards) {
    Sheet sheet = workbook.createSheet("站场信息");
    Row header = sheet.createRow(0);
    // 创建标题行
    header.createCell(0).setCellValue("站场名称");
    header.createCell(1).setCellValue("站场编码");
    header.createCell(2).setCellValue("站场类型");
    header.createCell(3).setCellValue("站场功能");
    header.createCell(4).setCellValue("等级");
    header.createCell(5).setCellValue("建设状态");
    fontStyle(workbook, header);
    // 填充数据
    for (int i = 0; i < facGasStationYards.size(); i++) {
        Row row = sheet.createRow(i + 1);
        FacGasStationYard yard = facGasStationYards.get(i);
        row.createCell(0).setCellValue(Optional.ofNullable(yard).map(FacGasStationYard::getStationName).orElse("") );
        row.createCell(1).setCellValue(Optional.ofNullable(yard).map(FacGasStationYard::getStationCode).orElse(""));
        row.createCell(2).setCellValue(Optional.ofNullable(yard).map(FacGasStationYard::getStationType).orElse(""));
        row.createCell(3).setCellValue(Optional.ofNullable(yard).map(FacGasStationYard::getStationFun).orElse(""));
        row.createCell(4).setCellValue(Optional.ofNullable(yard).map(FacGasStationYard::getGrade).orElse(""));
        row.createCell(5).setCellValue(Optional.ofNullable(yard).map(FacGasStationYard::getBuildStatus).orElse(""));
    }
}
// 设置 excel 字体样式
private static void fontStyle(Workbook workbook, Row header) {
    CellStyle cellStyle = workbook.createCellStyle();
    Font font = workbook.createFont();
    font.setBold(true);                                       // 加粗
    font.setFontHeightInPoints((short) 12);                   // 设置字体大小
    font.setFontName("微软雅黑");                              // 设置字体
    cellStyle.setFont(font);                                  // 向样式里添加字体设置
    // 将样式应用到标题行的每个单元格
    for (int i = 0; i < header.getPhysicalNumberOfCells(); i++) {
        header.getCell(i).setCellStyle(cellStyle);
    }
}