原创 | POI导入导出Excel工具类分享

640?wx_fmt=png

1 PoI中Excel组成

Excel的文件的组织形式,一个Excel文件对应于一个workbook(HSSFWorkbook),一个workbook可以有多个sheet(HSSFSheet)组成,一个sheet是由多个row(HSSFRow)组成,一个row是由多个cell(HSSFCell)组成。

2 业务流程

POI在项目中用于业务数据(比如说用户)的导入导出。分为三个部分:

(1)导出Excel模板,确保用户按照我们给的格式填写,这样方便导入

(2)导入填写好的模板,解析为数据集合

(3)导出数据库中的数据到表格

maven坐标:

        <!-- https://mvnrepository.com/artifact/org.apache.poi/poi -->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>3.17</version>
        </dependency>

3 导出excel模板

效果:

640?wx_fmt=png

业务代码:创建表格,创建标题、表头

    public Workbook createExcelTemplate() {

        HSSFWorkbook workbook = new HSSFWorkbook();

        //表头(每一列的名字以及填写要求)
        List<String> commonHeaders = Arrays.asList(
                "所在行政区域|(必填,例如:滨海新区)",
                "所在街道办事处|(必填,例如:辖南街道)",
                "所在道路及门牌号|(必填)", "小区名称|(必填)",
                "楼栋编号|(例如:1)", "单元数量|(例如:2)",
                "楼层数量|(例如:25)", "每层每个单元的户数|(例如:2)");

        ExcelTool.createSheetWithRichTextHeader(workbook, "template",
                ExcelTool.convertToRichText(workbook, commonHeaders));

        List<String> variableHeaders = Arrays.asList("小区基本信息|(必填)", "楼栋信息,每栋楼对应一行|(可以不填)");

        ExcelTool.createVariableCellSizeHeaderWithRichText(workbook, "template",
                ExcelTool.convertToRichText(workbook, variableHeaders), 4);

        //表格标题行
        ExcelTool.createTitleRow(workbook, "template", "小区信息表", 8);
        return workbook;
    }

工具类方法:创建富文本表头

    /**
     * 创建富文本表头
     * @param workbook 表格实体
     * @param headers 表头,使用"|"作为分隔符,分隔符前面的是黑色字体,分隔符后边将会变成红色字体,例如“属性名称|(属性填写要求)”
     * @return 富文本
     */
    public static List<RichTextString> convertToRichText(Workbook workbook, List<String> headers){

        List<RichTextString> richTextStrings = new ArrayList<>();

        for(String head : headers){
            String[] headSplit = StringUtils.split(head, "|");
            String wholeHead = StringUtils.remove(head, "|");
            RichTextString richTextString = new HSSFRichTextString(wholeHead);
            richTextString.applyFont(0, headSplit[0].length(), getHeadFont(workbook));
            if(headSplit.length > 1){
                richTextString.applyFont(headSplit[0].length() , wholeHead.length(), getMouldCellFont(workbook));
            }
            richTextStrings.add(richTextString);
        }
        return richTextStrings;
    }

4 导入数据

利用反射导入Excel,注意,表格中列的顺序应该和类中字段的顺序一致

    /**
     * 从表格中解析数据,注意表格中各个属性的顺序要和java类中一致,本工具类从第四行开始读数据,前三行默认为标题行
     * @param workbook
     * @param clazz
     * @return
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    public List<T> getDataFromExcel(Workbook workbook, Class<T> clazz) throws IllegalAccessException, InstantiationException {

        Field[] fields = clazz.getDeclaredFields();

        List<T> dataList = new ArrayList<>();
        // 解析sheet
        for (int i = 0; i < workbook.getNumberOfSheets(); i++) {
            Sheet sheet = workbook.getSheetAt(i);
            int readRowCount = sheet.getPhysicalNumberOfRows();
            // 解析sheet 的行
            for (int j = ROW_DATA_INDEX; j < readRowCount; j++) {
                Row row = sheet.getRow(j);
                if (row == null) {
                    continue;
                }
                if (row.getFirstCellNum() < 0) {
                    continue;
                }
                // 解析sheet 的列
                T t = clazz.newInstance();
                for (int k = 0; k < fields.length; k++) {
                    Cell cell = row.getCell(k);
                    Field field = fields[k];
                    field.setAccessible(true);
                    if(cell == null) {
                        continue;
                    }
                    cell.setCellType(CellType.STRING);
                    if(field.getType() == String.class){
                        field.set(t, cell.getStringCellValue());
                    } else if (field.getType() == Integer.class){
                        field.set(t, Integer.parseInt(cell.getStringCellValue()));
                    } else if(field.getType() == Double.class) {
                        field.set(t, Double.valueOf(cell.getStringCellValue()));
                    } else if(field.getType() == Float.class) {
                        field.set(t, Float.valueOf(cell.getStringCellValue()));
                    }
                    // todo 添加更多类型
                }
                dataList.add(t);
            }
        }
        return dataList;
    }

5、导出数据

业务代码:查询数据、创建表格、下载文件

        //查询数据
        List<WorkloadDTO>  villageQueryDTOS = reportService.queryWorkload(ip, index, days, sort, direction);
        String name = sm.getString("workload.name");
        String code = sm.getString("workload.code");
        String human = sm.getString("workload.human");
        String camera = sm.getString("workload.camera");

        //创建表格
        Workbook export = ExcelTool.createCommonWorkbookWithCommonHeader(villageQueryDTOS, Arrays.asList(name, code, human, camera), "工作量统计");

        //下载
        ExcelTool.downloadBrowser(export, response, sm.getString("export_workload.xls", sm.getString("days."+ days)), request);

工具类中方法:通过反射来导出对象中的所有一级属性

    /**
     * 导出对象中的所有一级属性
     * @param workbook
     * @param sheet
     * @param dataList
     * @return
     * @throws IllegalAccessException
     */
    public static Workbook createCommonWorkbook(Workbook workbook, Sheet sheet, List dataList) throws IllegalAccessException {
        CellStyle defaultCellStyle = ExcelTool.getCommonCellStyle(workbook);

        // 遍历集合数据,产生数据行
        Row row;
        int index = ROW_DATA_INDEX;
        for (Object data : dataList) {
            row = sheet.createRow(index);
            row.setHeightInPoints(25);
            int cellIndex = 0;
            //获取集合元素的类类型,也就是要下载的类的类类型
            Class c = data.getClass();

            Field[] fields = c.getDeclaredFields();
            //遍历属性名集合,获取每一个要导出的属性的名字
            for (Field field : fields) {

                //设置属性对象可读
                field.setAccessible(true);
                //获取data中该属性的值
                Object insertToCell = field.get(data);
                //创建单元格
                Cell cell = row.createCell(cellIndex++);
                //设置单元格类型为字符串
                cell.setCellType(CellType.STRING);
                cell.setCellStyle(defaultCellStyle);
                //插入数据
                if (insertToCell == null) {
                    cell.setCellValue("");
                } else {
                    cell.setCellValue("" + insertToCell);
                }
            }
            index++;
        }
        return workbook;
    }

工具类中方法:导出对象中的多级属性

    /**
     * 通用导出方法,可导出对象属性中的属性,层级数量不限
     * @param workbook
     * @param sheet
     * @param dataList 要导出的数据
     * @param fieldNames 属性,如果是属性套属性,则多级属性之间用点号分隔 如“attribute1.attribute2”;
     * @return
     * @throws NoSuchFieldException
     * @throws IllegalAccessException
     */
    public static Workbook createCommonWorkbook(Workbook workbook, Sheet sheet, List dataList, List<String> fieldNames) throws NoSuchFieldException, IllegalAccessException {
        CellStyle defaultCellStyle = ExcelTool.getCommonCellStyle(workbook);

        // 遍历集合数据,产生数据行
        Row row;
        int index = ROW_DATA_INDEX;
        for (Object data : dataList) {
            index++;
            row = sheet.createRow(index);
            row.setHeightInPoints(25);
            int cellIndex = 0;
            //获取集合元素的类类型,也就是要下载的类的类类型
            Class c = data.getClass();

            //遍历属性名集合,获取每一个要导出的属性的名字
            for (String fieldName : fieldNames) {

                //如果是属性中有好几级属性(该字段是引用类型),获取每一级属性的名字
                String[] fields = fieldName.split("\\.");

                //定义要插入到数据库中的数据对象
                Object insertToCell = null;
                for (int i = 0; i < fields.length; i++) {

                    //i== 0说明是第一级属性
                    if (i == 0) {
                        //从data中根据属性名获取属性对象
                        Field nameField = c.getDeclaredField(fields[i]);
                        //设置属性对象可读
                        nameField.setAccessible(true);
                        //获取data中该属性的值
                        insertToCell = nameField.get(data);
                        //如果第一级属性为null,则不解析下一级属性
                        if (insertToCell == null) {
                            break;
                        }
                    } else {
                        //从上一级属性值中获取属性对象
                        Field nameField = insertToCell.getClass().getDeclaredField(fields[i]);
                        //设置可读
                        nameField.setAccessible(true);
                        //获取上一级属性对象中该属性的值
                        insertToCell = nameField.get(insertToCell);
                    }
                }

                //创建单元格
                Cell cell = row.createCell(cellIndex++);
                //设置单元格类型为字符串
                cell.setCellType(CellType.STRING);
                cell.setCellStyle(defaultCellStyle);
                //插入数据
                if (insertToCell == null) {
                    cell.setCellValue("");
                } else {
                    cell.setCellValue("" + insertToCell);
                }
            }
        }

        return workbook;
    }

工具类方法:下载excel文件

    /**
     * 下载到客户端浏览器
     *
     * @param workbook 要下载的Excel表格文件
     * @param response 响应
     * @param fileName 文件名称
     * @throws IOException io流异常
     */
    public static void downloadBrowser(Workbook workbook, HttpServletResponse response, String fileName, HttpServletRequest request) throws IOException {
        request.setCharacterEncoding("UTF-8");

        // 清空response
        response.reset();

        // 设置response的响应头Header,控制浏览器以下载的形式打开文件
        response.setHeader("Content-disposition",
                "attachment;" + encodeFileName(request, fileName));
        //获得输出流,包装成缓冲流可以提高输入、输出效率。但需要写flush方法才能清空缓冲区
        OutputStream toClient = new BufferedOutputStream(
                response.getOutputStream());

        //设置文件类型和编码格式
        response.setContentType("application/vnd.ms-excel;charset=utf-8");

        //将表格数据直接写入到输出流
        workbook.write(toClient);

        //flush方法迫使缓冲的输出数据被写出到底层输出流中,其实其内部也是调用write方法
        toClient.flush();
        //关闭流
        toClient.close();
    }

注释在代码中比较完善了,就不一一赘述了。代码请见:

https://github.com/mambo-wang/tools/blob/master/ExcelTool.java

总结:

最近一年做的项目中多次用到了表格导入导出功能,因此总结出这样一个工具类,主要用到的poi功能有:

(1)创建表格、sheet、row、cell、富文本

(2)调整文字颜色、粗细、字体、字号

(3)合并单元格、调整单元格外框粗细

希望需要的人可以用得上~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值