一个工具,关于Excel导入和导出的DEMO,基于POI为基础来编写的一套小工具。这两个部分导入只举了一个很简单的例子,主要是Excel的导出,为了作为记录和学习,使用了自定义注解和反射。
Java操作Excel主要分为两个poi和jxl,本文只介绍了POI。
POI官网:http://poi.apache.org/
本DEMO使用官网3.15版本。
第一部分:POI读取Excel,这里只做了一个简单的DEMO,主要的重头戏在下面的导出一块,所以这部分各位看官看看就好。
package com.againfly.poi; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.text.SimpleDateFormat; import org.apache.poi.EncryptedDocumentException; import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.DateUtil; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.usermodel.WorkbookFactory; public class ExcelRead { /* * 读取指定的一个excel文件, * 获取所有sheet名称 * 指定第一个sheet,读取所有行内容 */ public static void main(String[] args) { Workbook wb = null; try { wb = ReadExcel("E://position.xls"); } catch (FileNotFoundException e) { e.printStackTrace(); } for (int i = 0; i < wb.getNumberOfSheets(); i++) { System.out.println(i + ":" + wb.getSheetName(i)); } Sheet sheet = wb.getSheetAt(0); int rowCount = sheet.getPhysicalNumberOfRows(); System.out.println("rowCount:" + rowCount); for (int i = 0; i < rowCount; i++) { ReadRow(sheet.getRow(i)); } } /** * 读取一个Excel文件,支持xls,xlsx * @param 文件路径名 */ public static Workbook ReadExcel(String path) throws FileNotFoundException { return ReadExcel(new File(path)); } /** * 读取一个Excel文件,支持xls,xlsx * jdk1.7之后开始支持try后带括号,括号内可以写需要finally框内需要关闭流的对象 * 前提是对象实现了Closeable接口 * 使用这种写法之后IO流可以不再finally内手动关闭。 * @param 文件对象 */ public static Workbook ReadExcel(File file) throws FileNotFoundException { if (null == file || !file.exists()) { throw new FileNotFoundException(); } Workbook wb = null; try (FileInputStream in = new FileInputStream(file)) { wb = WorkbookFactory.create(in); } catch (IOException | EncryptedDocumentException | InvalidFormatException e) { e.printStackTrace(); } return wb; } /** * 读取一行的内容 * @param row */ public static void ReadRow(Row row) { int cellCount = row.getPhysicalNumberOfCells(); // 获取总列数 for (int i = 0; i < cellCount; i++) { Cell cell = row.getCell(i); System.out.print(CellValue(cell) + "\t"); } System.out.println(); } public static SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd"); /** * 使用POI4.0版本时会失效。 目前使用POI3.15。 * 将单元格中的内容并转为String格式。 */ @SuppressWarnings("deprecation") public static String CellValue(Cell cell) { String space = ""; if (null == cell) { return space; } int cellType = cell.getCellType(); String cellValue = null; switch (cellType) { case Cell.CELL_TYPE_STRING: // 文本 cellValue = cell.getStringCellValue(); break; case Cell.CELL_TYPE_NUMERIC: // 数字、日期 if (DateUtil.isCellDateFormatted(cell)) { cellValue = fmt.format(cell.getDateCellValue()); // 日期型 } else { cellValue = String.valueOf(cell.getNumericCellValue()); // 数字 } break; case Cell.CELL_TYPE_BOOLEAN: // 布尔型 cellValue = String.valueOf(cell.getBooleanCellValue()); break; case Cell.CELL_TYPE_BLANK: // 空白 case Cell.CELL_TYPE_ERROR: // 错误 case Cell.CELL_TYPE_FORMULA: // 公式 default: cellValue = space; } return cellValue; } }
=====================华丽分割线========================
第二部分:POI导出Excel,这里做的稍微充分一点,但一样只是一个DEMO,使用了自定义注解和反射写的一个工具类。
先列出使用效果,再看实现逻辑:
实体类:Book.java,本代码中省略所有get和set方法。
package com.againfly.poi; import java.sql.Timestamp; public class Book { private int id; @ExcelField("姓名") private String name; @ExcelField(name = "时间", format = "yyyy年MM月dd日 HH:mm:ss") private Timestamp time; @ExcelField(serialize = false) private String UUID; }
执行方法:其实就是简单的生成了两个Book对象,并且放到了一个List里面,之后又使用了即将出现的Demo
package com.againfly.poi; import java.sql.Timestamp; import java.util.ArrayList; import java.util.List; import java.util.UUID; public class Main { public static void main(String[] args){ Book book = new Book(); book.setId(1); book.setName("巨人的陨落"); book.setTime(new Timestamp(System.currentTimeMillis())); book.setUUID(UUID.randomUUID().toString()); Book book2 = new Book(); book2.setId(2); book2.setName("每周工作四小时"); book2.setTime(new Timestamp(System.currentTimeMillis())); book2.setUUID(UUID.randomUUID().toString()); List<Book> list = new ArrayList<>(); list.add(book); list.add(book2); ExcelWrite<Book> excelWrite = new ExcelWrite<Book>(); excelWrite.init(list); excelWrite.outWook("E:/book.xls"); } }
输出效果图:
还是蛮复合我要的功能的,接下来开始贴出代码。
首先是自定义注解类:
package com.againfly.poi; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @Target(ElementType.FIELD):该注解只能注解在字段上 * @Retention(RetentionPolicy.RUNTIME):该注解可以保留到运行期,可以通过反射找到本注解值 * @Documented:表明这个注解应该被 javadoc工具记录 * @author Jecced */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ExcelField { /** * 导出的Excel的字段名称 * 当name不为空时,忽略value的值 */ public String name() default ""; /** * 导出的Excel的字段名称 * value的意思是字段的默认值 * 可以直接使用默认注解,但是不指定名称例如:@ExcelField("字段名") * 需要注意的是,当name不为空时,value字段自动失效。 */ public String value() default ""; /** * 是否对本字段进行序列化操作,默认值为true * 如果你对某个字段不需要进行序列化操作,可以设置本注解值为false * 这样就能忽略这个字段进行输出 * 例如:@ExcelField(serialize = false) */ public boolean serialize() default true; /** * 当一个字段为Date时,使用本字段可以序列化输出的时间格式 * 在一个属于Date的字段使用该注解属性时,无效 */ public String format() default ""; }
代码实现逻辑:ExcelWrite.java
package com.againfly.poi; import java.io.Closeable; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.lang.reflect.Field; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.HorizontalAlignment; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; public class ExcelWrite<T> { private List<Field> serializeFieldList = null; private List<T> data; /** * 初始化将要序列化的list数据 * 读取对象,通过反射查询需要的字段 */ public void init(List<T> list) { data = list; if (list.isEmpty()) { serializeFieldList = Collections.emptyList(); return; } T obj = list.get(0); Field[] fields = obj.getClass().getDeclaredFields(); serializeFieldList = new ArrayList<Field>(fields.length + 1); for (Field field : fields) { field.setAccessible(true); ExcelField excelField = field.getAnnotation(ExcelField.class); if (null == excelField || excelField.serialize()) { serializeFieldList.add(field); } } } private Workbook wb = null; private Sheet sheet = null; /** * 将文件输出到指定路径 */ public void outWook(String filePath) { File file = new File(filePath); outWook(file); file = null; } /** * 将文件输出到指定File文件 */ public void outWook(File file) { wb = new HSSFWorkbook(); createHead(); createRow(); FileOutputStream out = null; try { out = new FileOutputStream(file); wb.write(out); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { safeClose(out); safeClose(wb); } } /** * 自带工具,关闭流 * 让代码变的简洁 */ private static void safeClose(Closeable o) { if (null == o) return; try { o.close(); } catch (IOException e) { e.printStackTrace(); } } /** * 创建Excel的样式和首行头 * 通过读取字段名称和注解来生成 */ private void createHead() { sheet = wb.createSheet(); // sheet.setDefaultColumnWidth((short) serializeFieldList.size() * 200); sheet.setDefaultColumnWidth(20); // 生成一个样式 CellStyle style = wb.createCellStyle(); // style.setAlignment(HSSFCellStyle.ALIGN_CENTER); style.setAlignment(HorizontalAlignment.CENTER); Row row = sheet.createRow(0); for (int i = 0; i < serializeFieldList.size(); i++) { Cell cell = row.createCell((short) i); cell.setCellStyle(style); Field field = serializeFieldList.get(i); ExcelField excelField = field.getAnnotation(ExcelField.class); if (null == excelField || (excelField.name().trim().isEmpty() && excelField.value().trim().isEmpty())) { cell.setCellValue(field.getName()); } else if (!excelField.name().trim().isEmpty()) { cell.setCellValue(excelField.name()); } else { cell.setCellValue(excelField.value()); } } style = null; } /** * 创建内容 */ private void createRow() { for (int i = 1; i <= data.size(); i++) { Row row = sheet.createRow((short) i); for (int c = 0; c < serializeFieldList.size(); c++) { try { Field field = serializeFieldList.get(c); row.createCell((short) c).setCellValue(value(field, data.get(i - 1))); } catch (IllegalArgumentException e) { e.printStackTrace(); } } } } /** * 获取对象相应字段的值 * 如果该字段是Date类型,并且有format注解,则返回格式化后的日期 * 如果是Date类型,但是没有format注解,则使用默认的yyyy-MM-dd HH:mm:ss:SSS格式输出 */ private String value(Field field, T t) { field.setAccessible(true); Object obj = null; try { obj = field.get(t); } catch (IllegalArgumentException | IllegalAccessException e) { e.printStackTrace(); return ""; } if(null == obj){ return ""; } ExcelField excelField = field.getAnnotation(ExcelField.class); if(obj instanceof Date){ String format = "yyyy-MM-dd HH:mm:ss:SSS"; if(null != excelField && !excelField.format().trim().isEmpty()){ format = excelField.format().trim(); } SimpleDateFormat sdf = new SimpleDateFormat(format); Date date = (Date) obj; String value = sdf.format(date); sdf = null; return value; } return obj.toString(); } }
面对疾风吧,瓜娃子
很骚很骚
很骚很骚