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

avatar
作者
筋斗云
阅读量:4

1.POI入门教程链接

http://t.csdnimg.cn/Axn4Picon-default.png?t=N7T8http://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();        }    } } 

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!