Apache POI 使用Java处理Excel数据 进阶

1.POI入门教程链接

http://t.csdnimg.cn/Axn4Phttp://t.csdnimg.cn/Axn4P建议:从入门看起会更好理解POI对Excel数据的使用和处理

记得引入依赖:

复制代码
         <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>4.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>4.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml-schemas</artifactId>
            <version>4.0.1</version>
        </dependency>

2.POI对单元格样式处理

复制代码
// JUnit的@Test注解表示这是一个测试方法
    @Test
    void testExcel() throws IOException {
        // 创建一个新的XSSFWorkbook对象,这是操作Excel文件的核心类
        XSSFWorkbook workbook = new XSSFWorkbook();

        // 在工作簿中创建一个名为"sheet1"的工作表
        XSSFSheet sheet = workbook.createSheet("sheet1");

        // 在工作表中创建第三行(行索引从0开始)
        Row row = sheet.createRow(2);

        // 在该行中创建第三个单元格(列索引从0开始)
        Cell cell = row.createCell(2);

        // 设置单元格的值为"Hello World 2024"
        cell.setCellValue("Hello World 2024");

        // 创建一个单元格样式
        CellStyle cellStyle = workbook.createCellStyle();
        // 设置单元格的上下左右边框为细线
        cellStyle.setBorderTop(BorderStyle.THIN);
        cellStyle.setBorderBottom(BorderStyle.THIN);
        cellStyle.setBorderLeft(BorderStyle.THIN);
        cellStyle.setBorderRight(BorderStyle.THIN);

        // 创建一个字体对象
        Font font = workbook.createFont();
        // 设置字体为"宋体"
        font.setFontName("宋体");
        // 设置字体大小为32磅
        font.setFontHeightInPoints((short) 32);
        // 将字体应用到单元格样式中
        cellStyle.setFont(font);

        // 设置行高为50磅
        row.setHeightInPoints((short) 50);
        // 设置第三列的宽度为100 * 365(单位是1/256个字符宽度)
        sheet.setColumnWidth(2, 100 * 365);

        // 将单元格样式应用到单元格上
        cell.setCellStyle(cellStyle);

        // 创建一个文件输出流,用于将Excel文件写入磁盘
        FileOutputStream out = new FileOutputStream("D:\\Desktop\\JavaCode\\poi-demo\\src\\main\\resources\\test.xlsx");

        // 将工作簿写入输出流
        workbook.write(out);
        // 关闭输出流
        out.close();
    }

3.将图片写入Excel表格中

复制代码
  // JUnit的@Test注解表示这是一个测试方法
    @Test
    void testDrawExcel() throws IOException {
        // 创建一个新的XSSFWorkbook对象,这是操作Excel文件的核心类
        XSSFWorkbook workbook = new XSSFWorkbook();

        // 在工作簿中创建一个名为"sheet1"的工作表
        XSSFSheet sheet = workbook.createSheet("sheet1");

        // 创建一个文件输入流,用于读取图片文件
        FileInputStream in = new FileInputStream("D:\\Desktop\\test.jpg");

        // 创建一个字节数组,长度为图片文件的大小
        byte[] bytes = new byte[in.available()];
        // 读取图片文件到字节数组
        in.read(bytes);
        // 关闭输入流
        in.close();

        // 将图片添加到工作簿,并返回图片的索引
        int index = workbook.addPicture(bytes, Workbook.PICTURE_TYPE_JPEG);

        // 获取创建助手,用于创建绘图对象
        CreationHelper helper = workbook.getCreationHelper();
        // 创建绘图对象,用于在工作表中绘制图片
        XSSFDrawing drawing = sheet.createDrawingPatriarch();
        // 创建客户端锚点,用于指定图片的位置
        ClientAnchor anchor = helper.createClientAnchor();

        // 设置图片的起始行
        anchor.setRow1(0);
        // 设置图片的起始列
        anchor.setCol1(1);

        // 创建图片对象,并将其插入到工作表中
        XSSFPicture picture = drawing.createPicture(anchor, index);
        // 调整图片大小以适应单元格
        picture.resize();

        // 创建一个文件输出流,用于将Excel文件写入磁盘
        FileOutputStream out = new FileOutputStream("D:\\Desktop\\JavaCode\\poi-demo\\src\\main\\resources\\test.xlsx");

        // 将工作簿写入输出流
        workbook.write(out);
        // 关闭输出流
        out.close();
    }

4.使用模版Excel

如何想根据模版进行创建Excel表格。而不是直接覆盖创建新的Excel表格的方式:

复制代码
// JUnit的@Test注解表示这是一个测试方法
    @Test
    void testDrawExcel() throws IOException {
        // 使用Spring的ClassPathResource来加载类路径下的资源文件
        Resource resource = new ClassPathResource("test.xlsx");
        // 获取资源文件的FileInputStream
        FileInputStream resourceIn = new FileInputStream(resource.getFile());
        // 使用FileInputStream加载现有工作簿
        XSSFWorkbook workbook = new XSSFWorkbook(resourceIn);

        // 获取工作簿中的第一个工作表
        XSSFSheet sheet = workbook.getSheetAt(0);

        // 创建一个文件输入流,用于读取图片文件
        FileInputStream in = new FileInputStream("D:\\Desktop\\test.jpg");

        // 创建一个字节数组,长度为图片文件的大小
        byte[] bytes = new byte[in.available()];
        // 读取图片文件到字节数组
        in.read(bytes);
        // 关闭输入流
        in.close();

        // 将图片添加到工作簿,并返回图片的索引
        int index = workbook.addPicture(bytes, Workbook.PICTURE_TYPE_JPEG);

        // 获取创建助手,用于创建绘图对象
        CreationHelper helper = workbook.getCreationHelper();
        // 获取工作表中的绘图对象,如果没有则创建
        Drawing<?> drawing = sheet.createDrawingPatriarch();
        // 创建客户端锚点,用于指定图片的位置
        ClientAnchor anchor = helper.createClientAnchor();

        // 设置图片的起始行为第11行(行索引从0开始)
        anchor.setRow1(10);
        // 设置图片的起始列为第2列(列索引从0开始)
        anchor.setCol1(1);

        // 创建图片对象,并将其插入到工作表中
        Picture picture = drawing.createPicture(anchor, index);
        // 调整图片大小以适应单元格
        picture.resize();

        // 创建一个文件输出流,用于将Excel文件写入磁盘
        FileOutputStream out = new FileOutputStream("D:\\Desktop\\JavaCode\\poi-demo\\src\\main\\resources\\test.xlsx");

        // 将工作簿写入输出流
        workbook.write(out);
        // 关闭输出流
        out.close();
    }

5.自定义Excel工具类

(1)自定义注解

复制代码
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ExcelAttribute {
    /** 对应的列名称 */
    String name() default "";
 
    /** 列序号 */
    int sort();
 
    /** 字段类型对应的格式 */
    String format() default "";
 
}

(2) 导出工具类:

复制代码
public class ExcelExportUtil<T> {
 
    private int rowIndex;
    private int styleIndex;
    private String templatePath;
    private Class clazz;
    private  Field fields[];


    public ExcelExportUtil(Class clazz,int rowIndex,int styleIndex) {
        this.clazz = clazz;
        this.rowIndex = rowIndex;
        this.styleIndex = styleIndex;
        fields = clazz.getDeclaredFields();
   }
 
    /**
     * 基于注解导出
     */
    public void export(HttpServletResponse response,InputStream is, List<T> objs,String 
fileName) throws Exception {
 
        XSSFWorkbook workbook = new XSSFWorkbook(is);
        Sheet sheet = workbook.getSheetAt(0);
 
        CellStyle[] styles = getTemplateStyles(sheet.getRow(styleIndex));
 
        AtomicInteger datasAi = new AtomicInteger(rowIndex);
        for (T t : objs) {
            Row row = sheet.createRow(datasAi.getAndIncrement());
            for(int i=0;i<styles.length;i++) {
                Cell cell = row.createCell(i);
                cell.setCellStyle(styles[i]);
                for (Field field : fields) {
                    if(field.isAnnotationPresent(ExcelAttribute.class)){
                        field.setAccessible(true);
                        ExcelAttribute ea = field.getAnnotation(ExcelAttribute.class);
                        if(i == ea.sort()) {
                            cell.setCellValue(field.get(t).toString());
                       }
                   }
               }
           }
       }
        fileName = URLEncoder.encode(fileName, "UTF-8");
        response.setContentType("application/octet-stream");
        response.setHeader("content-disposition", "attachment;filename=" + new 
String(fileName.getBytes("ISO8859-1")));
        response.setHeader("filename", fileName);
        workbook.write(response.getOutputStream());
   }
 
    public CellStyle[] getTemplateStyles(Row row) {
        CellStyle [] styles = new CellStyle[row.getLastCellNum()];
        for(int i=0;i<row.getLastCellNum();i++) {
            styles[i] = row.getCell(i).getCellStyle();
       }
        return styles;
   }
}

(3)导入工具类:

复制代码
public class ExcelImportUtil<T> {
 
    private Class clazz;
    private  Field fields[];
 
    public ExcelImportUtil(Class clazz) {
        this.clazz = clazz;
        fields = clazz.getDeclaredFields();
   }
 
    /**
     * 基于注解读取excel
     */
    public List<T> readExcel(InputStream is, int rowIndex,int cellIndex) {
        List<T> list = new ArrayList<T>();
        T entity = null;
        try {
            XSSFWorkbook workbook = new XSSFWorkbook(is);
            Sheet sheet = workbook.getSheetAt(0);
            // 不准确
            int rowLength = sheet.getLastRowNum();
 
            System.out.println(sheet.getLastRowNum());
            for (int rowNum = rowIndex; rowNum <= sheet.getLastRowNum(); rowNum++) {
                Row row = sheet.getRow(rowNum);
                entity = (T) clazz.newInstance();
                System.out.println(row.getLastCellNum());
                for (int j = cellIndex; j < row.getLastCellNum(); j++) {
                    Cell cell = row.getCell(j);
                    for (Field field : fields) {
                        if(field.isAnnotationPresent(ExcelAttribute.class)){
                            field.setAccessible(true);
                            ExcelAttribute ea = 
field.getAnnotation(ExcelAttribute.class);
                            if(j == ea.sort()) {
                                field.set(entity, covertAttrType(field, cell));
                           }
                       }
                   }
               }
                list.add(entity);
           }
       } catch (Exception e) {
            e.printStackTrace();
       }
        return list;
   }
 
 
    /**
     * 类型转换 将cell 单元格格式转为 字段类型
*/
    private Object covertAttrType(Field field, Cell cell) throws Exception {
        String fieldType = field.getType().getSimpleName();
        if ("String".equals(fieldType)) {
            return getValue(cell);
       }else if ("Date".equals(fieldType)) {
            return new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").parse(getValue(cell)) ;
       }else if ("int".equals(fieldType) || "Integer".equals(fieldType)) {
            return Integer.parseInt(getValue(cell));
       }else if ("double".equals(fieldType) || "Double".equals(fieldType)) {
            return Double.parseDouble(getValue(cell));
       }else {
            return null;
       }
   }
 
 
    /**
     * 格式转为String
     * @param cell
     * @return
     */
    public String getValue(Cell cell) {
        if (cell == null) {
            return "";
       }
        switch (cell.getCellType()) {
            case STRING:
                return cell.getRichStringCellValue().getString().trim();
            case NUMERIC:
                if (DateUtil.isCellDateFormatted(cell)) {
                    Date dt = DateUtil.getJavaDate(cell.getNumericCellValue());
                    return new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(dt);
               } else {
                    // 防止数值变成科学计数法
                    String strCell = "";
                    Double num = cell.getNumericCellValue();
                    BigDecimal bd = new BigDecimal(num.toString());
                    if (bd != null) {
                        strCell = bd.toPlainString();
                   }
                    // 去除 浮点型 自动加的 .0
                    if (strCell.endsWith(".0")) {
                        strCell = strCell.substring(0, strCell.indexOf("."));
                   }
                    return strCell;
               }
            case BOOLEAN:
                return String.valueOf(cell.getBooleanCellValue());
            default:
                return "";
       }
   }

(4)工具类的使用

在User实体上对导出的字段名加上自定义注解@ExcelAttribute(序列号)

导入数据:

复制代码
List<User> list = new ExcelImportUtil(User.class).readExcel(is, 1, 2);

导出数据:

复制代码
@GetMapping("/export/{month}")
    public void export(@PathVariable(name = "month") String month) throws Exception {
        //1.构造数据
        List<EmployeeReportResult> list = 
userCompanyPersonalService.findByReport(companyId,month+"%");
 
        //2.加载模板流数据
        Resource resource = new ClassPathResource("excel-template/hr-demo.xlsx");
        FileInputStream fis = new FileInputStream(resource.getFile());
 
        new ExcelExportUtil(EmployeeReportResult.class,2,2).
                export(response,fis,list,"人事报表.xlsx");
   }

6.处理大量数据的文件(SXSSF)

(1)SXSSF和XSSF的区别

SXSSF和XSSF都是Apache POI库中用于处理Excel文件的API,它们属于POI项目中用于操作Excel 2007 OOXML (.xlsx)文件的组件。以下是SXSSF和XSSF的主要区别:

  1. 内存使用

    • XSSF:为处理大型Excel文件提供了完整的API支持,但它会将整个工作簿加载到内存中。这意味着对于非常大的Excel文件,它可能会消耗大量的Java堆内存。
    • SXSSF:是XSSF的流式扩展,目的是减少内存消耗。它不会将整个工作簿保留在内存中,而是仅保持一定数量的行在内存中,其余的行将被写入磁盘上的临时文件中。
  2. 性能

    • 使用XSSF处理非常大的Excel文件时,可能会遇到性能瓶颈,因为它需要更多的内存资源。
    • SXSSF在处理大型文件时性能更优,因为它使用了更少的内存,并且它的写入操作是流式的。
  3. 功能限制

    • XSSF提供了完整的Excel文件处理能力,但由于其内存占用较大,处理大型文件时可能会出现问题。
    • SXSSF由于使用了流式处理,因此有一些限制。例如,它不支持获取或设置单元格样式,也不支持插入或删除行。
  4. 使用场景

    • 如果您的应用程序需要处理中等大小的Excel文件,并且需要完整的Excel功能,XSSF是一个不错的选择。
    • 如果您需要处理大型Excel文件,并且可以接受SXSSF的一些限制,那么SXSSF可能是更好的选择。

(2)代码实现:

自定义处理器

复制代码
/自定义Sheet基于Sax的解析处理器
public class SheetHandler implements XSSFSheetXMLHandler.SheetContentsHandler {
 
    //封装实体对象
    private PoiEntity entity;
 
    /**
     * 解析行开始
     */
    @Override
    public void startRow(int rowNum) {
        if (rowNum >0 ) {
            entity = new PoiEntity();
       }
   }
 
    /* * 解析每一个单元格
     */
    @Override
    public void cell(String cellReference, String formattedValue, XSSFComment comment) 
    {
        if(entity != null) {
            switch (cellReference.substring(0, 1)) {
                case "A":
                    entity.setId(formattedValue);
                    break;
                case "B":
                    entity.setBreast(formattedValue);
                    break;
                case "C":
                    entity.setAdipocytes(formattedValue);
                    break;
                case "D":
                    entity.setNegative(formattedValue);
                    break;
                case "E":
                    entity.setStaining(formattedValue);
                    break;
                case "F":
                    entity.setSupportive(formattedValue);
                    break;
                default:
                    break;
           }
       }
   }
 
    /**
     * 解析行结束
     */
    public void endRow(int rowNum) {
        System.out.println(entity);
   }
 
    //处理头尾
    public void headerFooter(String text, boolean isHeader, String tagName) {
   }
}

自定义解析器

复制代码
/**
 * 自定义Excel解析器
 */
public class ExcelParser {
 
    public void parse (String path) throws Exception {
        //1.根据Excel获取OPCPackage对象
        OPCPackage pkg = OPCPackage.open(path, PackageAccess.READ);
        try {
            //2.创建XSSFReader对象
            XSSFReader reader = new XSSFReader(pkg);
            //3.获取SharedStringsTable对象
            SharedStringsTable sst = reader.getSharedStringsTable();
            //4.获取StylesTable对象
            StylesTable styles = reader.getStylesTable();
            //5.创建Sax的XmlReader对象
            XMLReader parser = XMLReaderFactory.createXMLReader();
            //6.设置处理器
            parser.setContentHandler(new XSSFSheetXMLHandler(styles,sst, new 
SheetHandler(), false));
            XSSFReader.SheetIterator sheets = (XSSFReader.SheetIterator) 
reader.getSheetsData();
            //7.逐行读取
            while (sheets.hasNext()) {
                InputStream sheetstream = sheets.next();
                InputSource sheetSource = new InputSource(sheetstream);
                try {
                    parser.parse(sheetSource);
               } finally {
                    sheetstream.close();
               }
           }
       } finally {
            pkg.close();
       }
   }
}
相关推荐
悟空码字19 分钟前
Spring Boot 整合 MongoDB 最佳实践:CRUD、分页、事务、索引全覆盖
java·spring boot·后端
皮皮林5512 天前
拒绝写重复代码,试试这套开源的 SpringBoot 组件,效率翻倍~
java·spring boot
用户908324602734 天前
Spring AI 1.1.2 + Neo4j:用知识图谱增强 RAG 检索(上篇:图谱构建)
java·spring boot
用户8307196840825 天前
Spring Boot 集成 RabbitMQ :8 个最佳实践,杜绝消息丢失与队列阻塞
spring boot·后端·rabbitmq
Java水解5 天前
Spring Boot 视图层与模板引擎
spring boot·后端
Java水解5 天前
一文搞懂 Spring Boot 默认数据库连接池 HikariCP
spring boot·后端
洋洋技术笔记5 天前
Spring Boot Web MVC配置详解
spring boot·后端
初次攀爬者6 天前
Kafka 基础介绍
spring boot·kafka·消息队列
用户8307196840826 天前
spring ai alibaba + nacos +mcp 实现mcp服务负载均衡调用实战
spring boot·spring·mcp