接下来讲讲当咱们遇到以下的excel文档,要求采纳这种模板形式导出
若每个单元格都设置款式的形式,则会很繁琐
那么咱们能不能读取该模板,获取外面的款式,导出的时候使用到呢?
一、应用模板打印
要想应用模板的形式读取款式,首先就获取到该模板,咱们的思路是
- 制作模版文件(模版文件的门路)
- 导入(加载)模版文件,从而失去一个工作簿
- 读取工作表
- 读取行
- 读取单元格
- 读取单元格款式
- 设置单元格内容
- 其余单元格应用读取款式
接下来让咱们代码实际实现思路,从获取模板开始....
@RequestMapping(value = "/export/{month}", method = RequestMethod.GET)public void export(@PathVariable(name = "month") String month) throws Exception { //1.结构数据 List<User> list = userService.findByReport(companyId,month+"%"); //2.加载模板流数据 Resource resource = new ClassPathResource("excel-template/hr-demo.xlsx"); FileInputStream fis = new FileInputStream(resource.getFile()); //3.依据文件流,加载指定的工作簿 XSSFWorkbook wb = new XSSFWorkbook(fis); //4.读取工作表 Sheet sheet = wb.getSheetAt(0); //5.抽取公共的款式 Row styleRow = sheet.getRow(2);//第三行 CellStyle [] styles = new CellStyle[styleRow.getLastCellNum()]; for(int i=0;i<styleRow.getLastCellNum();i++) { styles[i] = styleRow.getCell(i).getCellStyle(); }}
这时咱们曾经将Excel文档里的第三行单元格款式保留到CelStyle数组里
若咱们想应用的话,只须要依据对应数组下标for循环读取即可
@RequestMapping(value = "/export/{month}", method = RequestMethod.GET)public void export(@PathVariable(name = "month") String month) throws Exception { //6.构建cell单元格 Cell cell = null; int titleInext = 2; for (user report : list) { styleRow = sheet.createRow(titleInext++); //编号 cell = dataRow.createCell(0); cell.setCellValue(report.getUserId()); cell.setCellStyle(styles[0]); //姓名 cell = dataRow.createCell(1); cell.setCellValue(report.getUsername()); cell.setCellStyle(styles[1]); //手机 cell = dataRow.createCell(2); cell.setCellValue(report.getMobile()); cell.setCellStyle(styles[2]); } //7.输入文件下载 String fileName = URLEncoder.encode(month+"人员信息.xlsx", "UTF-8"); response.setContentType("application/octet-stream"); response.setHeader("content-disposition", "attachment;filename=" + newString(fileName.getBytes("ISO8859-1"))); response.setHeader("filename", fileName); workbook.write(response.getOutputStream()); }
再导出的话,咱们就能够领有对应的款式了
二、自定义工具类
应用模板打印时,咱们将模板里的第三行单元格款式存储到数组中
那么咱们采纳一个for循环的操作进行赋值
for (user report : list) { styleRow = sheet.createRow(titleInext++); for(int j=0;j<styles.length;j++){ cell = dataRow.createCell(j);//创立单元格 cell.setCellStyle(styles[0]);//赋值单元格款式 }}
然而咱们怎么晓得这个第一个单元格赋值ID、第二个单元格赋值名称呢?
咱们对于单元格的地位和属性,并且动静的应用对象属性赋值须要思考
咱们能够不能够应用注解的形式进行标注呢?
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.FIELD)public @interface ExcelAttribute { /** 对应的列名称 */ String name() default ""; /** 列序号 */ int sort(); /** 字段类型对应的格局 */ String format() default "";}
当咱们须要应用的时候则在User类应用注解形式来定位赋值的地位
class User{ @ExcelAttribute(sort = 0) private String name; @ExcelAttribute(sort = 1) private String tel; //省略其余关键性代码.....}
接下来咱们领会领会应用注解的形式获取值
//1.结构数据List<User> list = userService.findByReport(companyId,month+"%");//2.加载模板流数据Resource resource = new ClassPathResource("excel-template/hr-demo.xlsx");FileInputStream fis = new FileInputStream(resource.getFile());//3.获取User类的ClassClass clazz = User.class;//3.获取本人申明User的各种字段//包含public,protected,privateField fields[] = clazz.getDeclaredFields();//4.依据文件流,加载指定的工作簿XSSFWorkbook wb = new XSSFWorkbook(fis); //5.读取工作表Sheet sheet = wb.getSheetAt(0);//6.抽取公共的款式Row styleRow = sheet.getRow(2);//第三行CellStyle [] styles = new CellStyle[styleRow.getLastCellNum()];for(int i=0;i<styleRow.getLastCellNum();i++) { styles[i] = styleRow.getCell(i).getCellStyle();}//7.写入单元格表int titleInext = 2;for (user report : list) { Row row = sheet.createRow(titleInext++); for(int i=0;i<styles.length;i++) { Cell cell = row.createCell(i); cell.setCellStyle(styles[i]); //获取本人申明User的各种字段 for (Field field : fields) { //如果ExcelAttribute注解存在于此字段上 if(field.isAnnotationPresent(ExcelAttribute.class)){ //setAccessible = true意味着容许客户端领有超级权限 //比方Java对象序列化 或者 其余长久化机制等通常禁止的机制 field.setAccessible(true); //返回字段上的ExcelAttribute 注解 ExcelAttribute ea = field.getAnnotation(ExcelAttribute.class); //将字段上的排序序号进行匹配赋值 if(i == ea.sort()) { cell.setCellValue(field.get(t).toString()); } } } }}
这样的状况咱们只须要标注对应的字段地位款式即可,更加简洁代码
三、导入工具类
同理,依据导出的思路,咱们也能够将导入的形式也变成一个工具类
一样应用一个注解的形式来标注对应的单元格与字段地位
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.FIELD)public @interface ExcelAttribute { /** 对应的列名称 */ String name() default ""; /** 列序号 */ int sort(); /** 字段类型对应的格局 */ String format() default "";}
class User{ @ExcelAttribute(sort = 0) private String name; @ExcelAttribute(sort = 1) private String tel; //省略其余关键性代码.....}
咱们的目标是将单元格里的信息与字段进行匹配返回实体类汇合
那么对于单元格里的属性解决、以及实体类的字段类型咱们要进行转换
/** * 格局转为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 ""; }}
/** * 类型转换 将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; } //其余类型待增加......}
接下来咱们依据导出的思路,编写导入的形式将对应的单元格绑定实体类
//1.组装User的汇合List<User> list = new ArrayList<User>();//2.反射实体类User entity = null;//3.导入的文件InputStream流XSSFWorkbook workbook = new XSSFWorkbook(is);Sheet sheet = workbook.getSheetAt(0);//4.获取User类的ClassClass clazz = User.class;//5.获取本人申明User的各种字段//包含public,protected,privateField fields[] = clazz.getDeclaredFields();//6.定义初始行数与单元格int beginRow = 1;//从第几行开始读取int beginCell = 1;//从第几单元格开始读取Row row = null;//7.循环读取行数try { for (int rowNum = beginRow; rowNum <= sheet.getLastRowNum(); rowNum++) { //获取行 row = sheet.getRow(rowNum); //实例化对象 entity = (User)clazz.newInstance(); //循环读取每行的单元格数 for (int j = beginCell; 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()) { //应用covertAttrType进行类型转换 field.set(entity, covertAttrType(field, cell)); } } } } list.add(entity); }} catch (Exception e) { e.printStackTrace();}
这样咱们就能够应用工具类,将导入的excel文档读取单元格信息与实体类绑定
四、工具类整顿
依据咱们的思路提供整合导入工具类、导出工具类方便使用
//导入工具类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); 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 ""; } }}
@Getter@Setter//导出工具类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; }}
参考资料
黑马程序员:基于SaaS平台的iHRM刷脸登录实战开发(报表相干视频)