使用自定义注解+POI实现通用的表格导入导出

之前做过单表格的导入导出,现在由于项目的需要,对很多表都要执行导入导出功能。如果每张表都单独去写导入导出,未免太麻烦了一些,后来考虑做一个通用的导入导出。


1 首先要实现通用的导出功能
导出可以根据数据库中表的字段来导出,至于哪些字段需要导出,哪些不需要,可以通过注解来实现。
(1)首先定义导入导出的注解

/**
 * 导入导出配置
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
//@Documented
//@Component
public @interface ExcelConfig {
    /**
     * @return 表头显示名(如:id字段显示为"编号") 默认为字段名
     */
    String value() default "field";

    /**
     * @return 单元格宽度 默认-1(自动计算列宽)
     */
    short width() default -1;

    /**
     * 将单元格值进行转换后再导出:<br/>
     * 目前支持以下几种场景:<br/>
     * 1. 固定的数值转换为字符串值(如:1代表男,2代表女)<br/>
     * <b>表达式:</b> "s:1=男,2=女"<br/>
     *
     * 2. 数值对应的值需要查询数据库才能进行映射(实现org.wuwz.poi.convert.ExportConvert接口)<br/>
     * <b>表达式:</b> "c:org.wuwz.poi.test.GradeIdConvert"
     *
     * @return 默认不启用
     */
    String convert() default "";


    /**
     * @return 当前单元格的字体颜色 (默认 HSSFColor.BLACK.index)
     */
    short color() default HSSFColor.BLACK.index;


    /**
     * 将单元格的值替换为当前配置的值:<br/>
     * 应用场景: <br/>
     * 密码字段导出为:"******"
     *
     * @return 默认true
     */
    String replace() default "";


    int colIndex() default -1;//排序

    boolean nullable() default true;//可以为空?

    boolean isExportData() default true;//是否导出

    int formatTime() default 0;//是否时间转换,是的话由时间字符串转时间戳
}

这里定义了一些基本的值,都有注释,自己去看。
(2)然后在需要执行导出功能的类中加上该注解

@ExcelConfig(value = "商品名称")
private String goods_name;
@ExcelConfig(value = "品牌ID",width = 100)
private Integer brand_id;

(3)下面我们定义一个工具类,专门用于处理导入导出功能

public class ExcelKit {
      private Class<?> mClass = null;
    private HttpServletResponse mResponse = null;
    // 默认以此值填充空单元格,可通过 setEmptyCellValue(string)改变其默认值。
    private String mEmptyCellValue = null;
    // 分Sheet机制:每个Sheet最多多少条数据
    private Integer mMaxSheetRecords = 10000;
    // 缓存数据格式器实例,避免多次使用反射进行实例化
    private Map<String, ExcelConvert> mConvertInstanceCache = new HashMap<>();

    protected ExcelKit() {
    }

    protected ExcelKit(Class<?> clazz) {
        this(clazz, null);
    }

    protected ExcelKit(Class<?> clazz, HttpServletResponse response) {
        this.mResponse = response;
        this.mClass = clazz;
    }
     /**
     * 用于浏览器导出
     *
     * @param clazz    实体Class对象
     * @param response 原生HttpServletResponse对象
     * @return ExcelKit
     */
    public static ExcelKit $Export(Class<?> clazz, HttpServletResponse response) {
        return new ExcelKit(clazz, response);
    }

     /**
     * 导出Excel(此方式需依赖浏览器实现文件下载,故应先使用$Export()构造器)
     *
     * @param data      数据集合
     * @param sheetName 工作表名字
     * @return true-操作成功,false-操作失败
     */
    public boolean toExcel(List<?> data, String sheetName) {
        required$ExportParams();

        try {
            return toExcel(data, sheetName, mResponse.getOutputStream());
        } catch (IOException e) {
            log.error("导出Excel失败:" + e.getMessage(), e);
        }
        return false;
    }
/**
     * 针对转换方法的默认实现(提供默认样式和文件命名规则)
     *
     * @param data      数据集合
     * @param sheetName 工作表名字
     * @param out       输出流
     * @return true-操作成功,false-操作失败
     */
    public boolean toExcel(List<?> data, String sheetName, OutputStream out) {

        return toExcel(data, sheetName, new ExportHandler() {

            @Override
            public CellStyle headCellStyle(SXSSFWorkbook wb) {
                CellStyle cellStyle = wb.createCellStyle();
                Font font = wb.createFont();
                cellStyle.setFillForegroundColor((short) 12);
                cellStyle.setFillPattern(CellStyle.SOLID_FOREGROUND);// 填充模式
                cellStyle.setBorderTop(CellStyle.BORDER_THIN);// 上边框为细边框
                cellStyle.setBorderRight(CellStyle.BORDER_THIN);// 右边框为细边框
                cellStyle.setBorderBottom(CellStyle.BORDER_THIN);// 下边框为细边框
                cellStyle.setBorderLeft(CellStyle.BORDER_THIN);// 左边框为细边框
                cellStyle.setAlignment(CellStyle.ALIGN_LEFT);// 对齐
                cellStyle.setFillForegroundColor(HSSFColor.GREEN.index);
                cellStyle.setFillBackgroundColor(HSSFColor.GREEN.index);
                font.setBoldweight(Font.BOLDWEIGHT_NORMAL);
                // font.setFontHeightInPoints((short) 12);// 字体大小
                font.setColor(HSSFColor.WHITE.index);
                // 应用标题字体到标题样式
                cellStyle.setFont(font);
                return cellStyle;
            }

            @Override
            public String exportFileName(String sheetName) {
                return String.format("导出-%s-%s", sheetName, System.currentTimeMillis());
            }
        }, out);
    }

    public boolean toExcel(List<?> data, String sheetName, ExportHandler handler, OutputStream out) {
        required$BuilderParams();
        long begin = System.currentTimeMillis();

//        if (data == null || data.size() < 1) {
//            log.error("没有检测到数据,不执行导出操作。");
//            return false;
//        }
        if (data == null) {
            log.error("没有检测到数据,不执行导出操作。");
            return false;
        }

        log.info(String.format("即将导出excel数据:%s条,请稍后..", data.size()));

        // 导出列查询。
        ExcelConfig currentExportConfig = null;
        ExcelItem currentExportItem = null;
        List<ExcelItem> exportItems = new ArrayList<>();
        for (Field field : mClass.getDeclaredFields()) {

            currentExportConfig = field.getAnnotation(ExcelConfig.class);
            if (currentExportConfig != null&&currentExportConfig.isExportData()) {
                currentExportItem = new ExcelItem()
                        .setField(field.getName())
                        .setDisplay("field".equals(currentExportConfig.value()) ? field.getName() : currentExportConfig.value())
                        .setWidth(currentExportConfig.width())
                        .setConvert(currentExportConfig.convert())
                        .setColor(currentExportConfig.color())
                        .setReplace(currentExportConfig.replace());
                exportItems.add(currentExportItem);
            }

            currentExportItem = null;
            currentExportConfig = null;
        }

        // 创建新的工作薄。
        SXSSFWorkbook wb = POIUtil.newSXSSFWorkbook();

        double sheetNo = Math.ceil(data.size() / mMaxSheetRecords);// 取出一共有多少个sheet.

        // =====多sheet生成填充数据=====
        for (int index = 0; index <= (sheetNo == 0.0 ? sheetNo : sheetNo - 1); index++) {
            SXSSFSheet sheet = POIUtil.newSXSSFSheet(wb, sheetName + (index == 0 ? "" : "_" + index));

            // 创建表头
            SXSSFRow headerRow = POIUtil.newSXSSFRow(sheet, 0);
            for (int i = 0; i < exportItems.size(); i++) {
                SXSSFCell cell = POIUtil.newSXSSFCell(headerRow, i);
                POIUtil.setColumnWidth(sheet, i, exportItems.get(i).getWidth(), exportItems.get(i).getDisplay());
                cell.setCellValue(exportItems.get(i).getDisplay());

                CellStyle style = handler.headCellStyle(wb);
                if (style != null) {
                    cell.setCellStyle(style);
                }
            }

            // 产生数据行
            if (data != null && data.size() > 0) {

                int startNo = index * mMaxSheetRecords;
                int endNo = Math.min(startNo + mMaxSheetRecords, data.size());
                for (int i = startNo; i < endNo; i++) {
                    SXSSFRow bodyRow = POIUtil.newSXSSFRow(sheet, i + 1 - startNo);

                    for (int j = 0; j < exportItems.size(); j++) {
                        // 处理单元格值
                        String cellValue = exportItems.get(j).getReplace();
                        if ("".equals(cellValue)) {
                            try {
                                cellValue = BeanUtils.getProperty(data.get(i), exportItems.get(j).getField());
                            } catch (Exception e) {
                                log.error("获取" + exportItems.get(j).getField() + "的值失败.", e);
                            }
                        }

                        // 格式化单元格值
                        if (!"".equals(exportItems.get(j).getConvert())) {
                            cellValue = convertCellValue(Integer.parseInt(cellValue), exportItems.get(j).getConvert());
                        }

                        // 单元格宽度
                        POIUtil.setColumnWidth(sheet, j, exportItems.get(j).getWidth(), cellValue);

                        SXSSFCell cell = POIUtil.newSXSSFCell(bodyRow, j);
                        if (cellValue == null) {
                            continue;
                        }
                        // fix: 当值为“”时,当前index的cell会失效
                        try {
                            cell.setCellValue("".equals(cellValue) ? null : cellValue);
                        } catch (Exception e) {
//                            e.printStackTrace();
                        }
                        CellStyle style = wb.createCellStyle();
                        Font font = wb.createFont();
                        font.setColor(exportItems.get(j).getColor());
                        style.setFont(font);
//                        style.setWrapText(true);
                        cell.setCellStyle(style);
                    }
                }
            }
        }

        try {
            // 生成Excel文件并下载.(通过response对象是否为空来判定是使用浏览器下载还是直接写入到output中)
            POIUtil.writeByLocalOrBrowser(mResponse, handler.exportFileName(sheetName), wb, out);
        } catch (Exception e) {
            log.error("生成Excel文件失败:" + e.getMessage(), e);
            return false;
        }

        log.info(String.format("Excel处理完成,共生成数据:%s行 (不包含表头),耗时:%s seconds.", (data != null ? data.size() : 0),
                (System.currentTimeMillis() - begin) / 1000F));
        return true;
    }
}

(4)在需要导出的地方,只需要一行代码就可以实现

ExcelKit.$Export(this.entityClass, response).toExcel(List<数据集合>, 导出文件名);

2 导入功能会根据表头的文字和注解中的文字进行匹配,然后根据注解中的导入顺序,实现导入
(1)首先还是实体

@ExcelConfig(value = "商品名称",colIndex = 1)
private String goods_name;              //商品的名称

这里的colIndex就是导入的顺序
(2)然后定义导入的方法

/**
     * 导入操作
     * @param clazz
     * @param xls
     * @return
     * @throws Exception
     */
    public static List importExcel(Class<?> clazz, InputStream xls) throws Exception{
        try {
            // 取得Excel
//            HSSFWorkbook wb = new HSSFWorkbook(xls);
//            HSSFSheet sheet = wb.getSheetAt(0);
            Workbook wb = WorkbookFactory.create(xls);
            Sheet sheet = wb.getSheetAt(0);
            Field[] fields = clazz.getDeclaredFields();
            List<Field> fieldList = new ArrayList<Field>(fields.length);
            for (Field field : fields) {
                if (field.isAnnotationPresent(ExcelConfig.class)) {
                    ExcelConfig modelProp = field.getAnnotation(ExcelConfig.class);
                    if (modelProp.colIndex() != -1) {
                        fieldList.add(field);
                    }
                }
            }
            // 行循环
            List<AbstractPageEntity> modelList = new ArrayList<AbstractPageEntity>(sheet.getPhysicalNumberOfRows() * 2);
            for (int i = 1; i < sheet.getPhysicalNumberOfRows(); i++) {
                // 数据模型
                AbstractPageEntity model = (AbstractPageEntity) clazz.newInstance();
                int nullCount = 0;
                Exception nullError = null;
                for (Field field : fieldList) {
                    ExcelConfig modelProp = field.getAnnotation(ExcelConfig.class);
//                    HSSFCell cell = sheet.getRow(i).getCell(modelProp.colIndex());
                    Cell cell = sheet.getRow(i).getCell(modelProp.colIndex()-1);
                    try {
                        if (cell == null || cell.toString().length() == 0) {
                            nullCount++;
                            if (!modelProp.nullable()) {
                                nullError = new Exception(StringUtil.format(notnullerror,
                                        new String[]{"" + (1 + i), modelProp.value(), modelProp.value()}));

                            }
                        } else if (field.getType().equals(Date.class)) {
                            if (Cell.CELL_TYPE_STRING == cell.getCellType()) {
                                BeanUtils.setProperty(model, field.getName(), WpDate.strToLongDate(String.valueOf(cell).trim()));
                            } else {
                                BeanUtils.setProperty(model, field.getName(), new Date(WpDate.strToLongDate(String.valueOf(cell).trim()).getTime()));

                            }
                        } else if (field.getType().equals(Timestamp.class)) {
                            if (Cell.CELL_TYPE_STRING == cell.getCellType()) {
                                BeanUtils.setProperty(model, field.getName(),
                                        new Timestamp(WpDate.strToLongDate(String.valueOf(cell).trim()).getTime()));
                            } else {
                                BeanUtils.setProperty(model, field.getName(),
                                        new Timestamp(cell.getDateCellValue().getTime()));
                            }

                        } else if (field.getType().equals(java.sql.Date.class)) {
                            if (Cell.CELL_TYPE_STRING == cell.getCellType()) {
                                BeanUtils.setProperty(model, field.getName(),
                                        new java.sql.Date(WpDate.strToLongDate(String.valueOf(cell).trim()).getTime()));
                            } else {
                                BeanUtils.setProperty(model, field.getName(),
                                        new java.sql.Date(cell.getDateCellValue().getTime()));
                            }
                        } else if (field.getType().equals(java.lang.Integer.class)) {
                            if (Cell.CELL_TYPE_NUMERIC == cell.getCellType()) {
                                BeanUtils.setProperty(model, field.getName(), (int) cell.getNumericCellValue());
                            } else if (Cell.CELL_TYPE_STRING == cell.getCellType()&&modelProp.formatTime()==0) {
                                BeanUtils.setProperty(model, field.getName(), Integer.parseInt(String.valueOf(cell).trim()));
                            } else if (Cell.CELL_TYPE_STRING == cell.getCellType()&&modelProp.formatTime()==1) {
                                BeanUtils.setProperty(model, field.getName(), WpDate.dateTostamp(WpDate.strToLongDate(String.valueOf(cell).trim())));
                            }
                        } else if (field.getType().equals(java.math.BigDecimal.class)) {
                            if (Cell.CELL_TYPE_NUMERIC == cell.getCellType()) {
                                BeanUtils.setProperty(model, field.getName(),
                                        new BigDecimal(cell.getNumericCellValue()));
                            } else if (Cell.CELL_TYPE_STRING == cell.getCellType()) {
                                BeanUtils.setProperty(model, field.getName(), new BigDecimal(String.valueOf(cell).trim()));
                            }
                        } else {
                            if (Cell.CELL_TYPE_NUMERIC == cell.getCellType()) {
                                BeanUtils.setProperty(model, field.getName(),
                                        new BigDecimal(cell.getNumericCellValue()));
                            } else if (Cell.CELL_TYPE_STRING == cell.getCellType()) {
                                BeanUtils.setProperty(model, field.getName(), String.valueOf(cell).trim());
                            }
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                        throw new Exception(StringUtil.format(errormsg, new String[]{"" + (1 + i), modelProp.value()})
                                + "," + e.getMessage());
                    }
                }
                if (nullCount == fieldList.size()) {
                    break;
                }
                if (nullError != null) {
                    throw nullError;
                }
                modelList.add(model);
            }
            return modelList;
        }catch (OpenException e){
            e.printStackTrace();
        }finally{
            xls.close();
        }
        return null;
    }

这里利用到了反射的机制。
(2)然后在需要导入的地方,用一行代码实现

 ExcelKit.importExcel(entityClass, file.getInputStream());
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值