关于java:我所知道报表之POI使用模板打印自定义工具类工具类整理

35次阅读

共计 9940 个字符,预计需要花费 25 分钟才能阅读完成。

接下来讲讲当咱们遇到以下的 excel 文档,要求采纳这种模板形式导出

若每个单元格都设置款式的形式,则会很繁琐

那么咱们能不能读取该模板,获取外面的款式,导出的时候使用到呢?

一、应用模板打印


要想应用模板的形式读取款式,首先就获取到该模板,咱们的思路是

  1. 制作模版文件(模版文件的门路)
  2. 导入(加载)模版文件,从而失去一个工作簿
  3. 读取工作表
  4. 读取行
  5. 读取单元格
  6. 读取单元格款式
  7. 设置单元格内容
  8. 其余单元格应用读取款式

接下来让咱们代码实际实现思路,从获取模板开始 ….

@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=" + new
String(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 类的 Class
Class clazz = User.class;

//3. 获取本人申明 User 的各种字段
// 包含 public,protected,private
Field 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 类的 Class
Class clazz = User.class;

//5. 获取本人申明 User 的各种字段
// 包含 public,protected,private
Field 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 刷脸登录实战开发(报表相干视频)

正文完
 0