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注解表示这是一个测试方法@Testvoid 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注解表示这是一个测试方法@Testvoid 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注解表示这是一个测试方法@Testvoid testDrawExcel() throws IOException {// 使用Spring的ClassPathResource来加载类路径下的资源文件Resource resource = new ClassPathResource("test.xlsx");// 获取资源文件的FileInputStreamFileInputStream 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();}// 去除 浮点型 自动加的 .0if (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的主要区别:
-
内存使用:
- XSSF:为处理大型Excel文件提供了完整的API支持,但它会将整个工作簿加载到内存中。这意味着对于非常大的Excel文件,它可能会消耗大量的Java堆内存。
- SXSSF:是XSSF的流式扩展,目的是减少内存消耗。它不会将整个工作簿保留在内存中,而是仅保持一定数量的行在内存中,其余的行将被写入磁盘上的临时文件中。
-
性能:
- 使用XSSF处理非常大的Excel文件时,可能会遇到性能瓶颈,因为它需要更多的内存资源。
- SXSSF在处理大型文件时性能更优,因为它使用了更少的内存,并且它的写入操作是流式的。
-
功能限制:
- XSSF提供了完整的Excel文件处理能力,但由于其内存占用较大,处理大型文件时可能会出现问题。
- SXSSF由于使用了流式处理,因此有一些限制。例如,它不支持获取或设置单元格样式,也不支持插入或删除行。
-
使用场景:
- 如果您的应用程序需要处理中等大小的Excel文件,并且需要完整的Excel功能,XSSF是一个不错的选择。
- 如果您需要处理大型Excel文件,并且可以接受SXSSF的一些限制,那么SXSSF可能是更好的选择。
(2)代码实现:
自定义处理器
/自定义Sheet基于Sax的解析处理器
public class SheetHandler implements XSSFSheetXMLHandler.SheetContentsHandler {//封装实体对象private PoiEntity entity;/*** 解析行开始*/@Overridepublic void startRow(int rowNum) {if (rowNum >0 ) {entity = new PoiEntity();}}/* * 解析每一个单元格*/@Overridepublic 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();}}
}