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();
       }
   }
}
相关推荐
飞翔的佩奇26 分钟前
Java项目: 基于SpringBoot+mybatis+maven校园资料分享平台(含源码+数据库+答辩PPT+毕业论文)
java·spring boot·spring·毕业设计·maven·mybatis·校园资料分享平台
西岭千秋雪_1 小时前
谷粒商城のElasticsearch
java·大数据·服务器·spring boot·elasticsearch·搜索引擎
zhang-ge2 小时前
python提取pdf表格到excel:拆分、提取、合并
python·pdf·excel
ʚɞ4962 小时前
k8s环境下的相关操作
运维·excel
Flying_Fish_roe3 小时前
Spring Boot- 数据库相关问题
java·spring boot·后端
终末圆3 小时前
MyBatis 增删改查【后端 17】
java·服务器·数据库·b树·mysql·spring·mybatis
GISer_Jing3 小时前
【前后端】大文件切片上传
前端·spring boot
qq_35323353893 小时前
【原创】java+springboot+mysql校园订餐网系统设计与实现
java·spring boot·mysql
计算机学姐3 小时前
基于微信小程序的高校实验室管理系统的设计与实现
java·vue.js·spring boot·mysql·微信小程序·小程序·intellij-idea
从零开始的-CodeNinja之路4 小时前
【Spring Boot】SpringBoot自动装配-Import
java·spring boot·后端