java 利用poi根据excel模板导出数据(一)

前言

作为B端开发,导出数据是不可以避免的,但是有时候需求很变态,表头复杂的一笔,各种合并单元格,如下图:

 这些虽说用代码可以实现,但是很繁琐,而且代码并不能通用,遇到更复杂的更难受。为了追求更简单,高效率,我们需要换个方法——-利用模板导出数据

?????能用模板?????

sure!

本文所用的excel样式,如果列更多、更复杂,直接套用就行  (下方图片中的数字是列名),行序号为1的是表格标题名称,行序号为2的是适应导出时把条件也导出来的情况,别怀疑,就是有这种需求!!

正文开始

1.需要用到的包

 poi

  <!-- json4excel,poi -->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>4.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>4.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml-schemas</artifactId>
            <version>4.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>ooxml-schemas</artifactId>
            <version>1.3</version>
        </dependency>

nutz(工具包,非必须,公司框架,本文与之相关的就只有一个NutMap==Map<String,Object>,不用这个请手动替换)

   <!-- https://mvnrepository.com/artifact/org.nutz/nutz -->
        <dependency>
            <groupId>org.nutz</groupId>
            <artifactId>nutz</artifactId>
            <version>1.r.68.v20200427</version>
        </dependency>

2.直接上代码

  入口

    public static void doExportLongArrearsData() {
        // 要导出的数据
        NutMap nutMap = NutMap.NEW();
        nutMap.addv("comm","1111");
        nutMap.addv("a","1111");
        nutMap.addv("b","2222");
        nutMap.addv("c","11333311");
        nutMap.addv("d","1114441");
        nutMap.addv("e","555");
        nutMap.addv("f","6666");
        nutMap.addv("g","7777");
        nutMap.addv("h","88888");
        List<NutMap> list = Lists.newArrayList();
        list.add(nutMap);
        // 导出列  列数
        int colNum = 9;
        int[] colWidth = new int[colNum];
        for (int i = 0; i < colNum; i++) {
            colWidth[i] = 23;
        }
        // 从XX行开始为数据内容  excel 第一行为0
        int startRow = 5;
        //  2003版本的Excel (xls) ---- HSSFWorkbook
        //    2007版本以及更高版本 (xlsx)---- XSSFWorkbook
        XSSFWorkbook workbook = null;
        try {
            // 此处linux和windows通用   /files/cq.xlsx 在resource目录下  视情况而定

        /**
             *   特殊说明: this.getClass().getResourceAsStream 
             *   如果fileUrl路径前不加 / 那么会读取类文件夹下的文件。加了才会读取resource下面的文件
             *   exp: this.getClass().getResourceAsStream("/files/cq.xlsx") ==>读取resource下面的文件
             *   this.getClass().getResourceAsStream("files/cq.xlsx") ==>读取当前类下的文件
             *   源码:
             *     private String resolveName(String name) {
             *         if (name == null) {
             *             return name;
             *         }
             *         if (!name.startsWith("/")) {
             *             Class<?> c = this;
             *             while (c.isArray()) {
             *                 c = c.getComponentType();
             *             }
             *             String baseName = c.getName();
             *             int index = baseName.lastIndexOf('.');
             *             if (index != -1) {
             *                 name = baseName.substring(0, index).replace('.', '/')
             *                     +"/"+name;
             *             }
             *         } else {
             *             name = name.substring(1);
             *         }
             *         return name;
             *     }
             */
            //  InputStream inputStream = this.getClass().getResourceAsStream("/files/cq.xlsx");
            FileInputStream inputStream = new FileInputStream( new File("C:\\Users\\usaer\\Desktop\\buss.xlsx"));
            workbook = new XSSFWorkbook(inputStream);

            // 获取sheet
            XSSFSheet sheetAt = workbook.getSheetAt(0);

            // 动态列  修改表头名 、修改模板数据等操作
            // 自定义参数
            int k = 1;
            updateCellLoad(workbook,sheetAt,k);

            // 填充数据
            fillBodyData( sheetAt,startRow,list,colWidth);

            // 设置单元格宽度 (不设置的话,就是模板的宽度)
            if (null != colWidth) {
                for (int i = 0; i < colWidth.length; i++) {
                    sheetAt.setColumnWidth(i, colWidth[i] * 256 + 184);
                }
            }

            // 输出流
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            // excel工作空间写入流
            workbook.write(byteArrayOutputStream);
            InputStream wrap = Streams.wrap(byteArrayOutputStream.toByteArray());
            // 写到本地
            writeToLocal("C:\\Users\\usaer\\Desktop\\buss_"+k+".xlsx",wrap);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        } finally {
            // 关闭流
            if (null != workbook)
                try {
                    workbook.close();
                }
                catch (IOException e) {

                }
        }
    }

修改列的方法 (填充数据之前之后都可以修改内容,包括但不限于修改列、修改行、样式等)


    public static void updateCellLoad( XSSFWorkbook workbook,Sheet sheet,int k) {
        try {
            //设置行 指定行 
            Row titlerow=sheet.getRow(2);
            //根据索引获取对应的列   如果合并单元格 有可能第一列就找不到  根据需求修改参数值
            Cell cell=titlerow.getCell(0, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
            //设置列的类型是字符串
            cell.setCellType(CellType.STRING);
            cell.setCellValue(k==1?"1":"0");
            String titleValue=cell.getStringCellValue();
            System.out.println(titleValue);


            // 设置行 指定行修改数据和样式 有可能第一行就找不到  根据需求修改参数值
            CellStyle style1 = workbook.createCellStyle();
            // 自动换行
            style1.setWrapText(true);
            Row row1 = sheet.createRow(1);
            // 行高度
            row1.setHeight(Short.parseShort("1000"));
            Cell row1Cell = row1.createCell(0, CellType.STRING);
            row1Cell.setCellStyle(style1);
            StringBuilder sb = new StringBuilder();
            sb.append("条件1:").append("ABC").append("\r\n");
            sb.append("条件2:").append("DEF").append("\r\n");
            sb.append("条件3:").append("GHI").append("\r\n");
            row1Cell.setCellValue(sb.toString());

        } catch (EncryptedDocumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

填充数据的方法

private static void fillBodyData(Sheet sheet, int startRow, List<NutMap> bodyList, int[] colWidth) {

        // 碰到 时间、金额等需要转换的  在setCellValue 里面操作
        for (int rowNum = 0; rowNum < bodyList.size(); rowNum++) {
            NutMap map = bodyList.get(rowNum);
            Row row = sheet.createRow(rowNum + startRow);
            int colNum = 0;
            // comm
            Cell accountNumberCell = row.createCell(colNum++, CellType.STRING);
            String accountNumber = map.getString("comm");
            colWidth[colNum - 1] = Math.max(colWidth[colNum - 1], Strings.charLength(accountNumber));
            accountNumberCell.setCellValue(accountNumber);
            // a
            Cell accountNameCell = row.createCell(colNum++, CellType.STRING);
            String accountName = map.getString("a");
            colWidth[colNum - 1] = Math.max(colWidth[colNum - 1], Strings.charLength(accountName));
            accountNameCell.setCellValue(accountName);
            // b
            Cell mobileCell = row.createCell(colNum++, CellType.STRING);
            String mobile = map.getString("b");
            colWidth[colNum - 1] = Math.max(colWidth[colNum - 1], Strings.charLength(mobile));
            mobileCell.setCellValue(mobile);

            // c
            Cell addressCell = row.createCell(colNum++, CellType.STRING);
            String address = map.getString("c");
            colWidth[colNum - 1] = Math.max(colWidth[colNum - 1], Strings.charLength(address));
            addressCell.setCellValue(address);

            // d
            Cell meterNoCell = row.createCell(colNum++, CellType.STRING);
            String meterNo = map.getString("d");
            meterNoCell.setCellValue(meterNo);
            colWidth[colNum - 1] = Math.max(colWidth[colNum - 1], Strings.charLength(meterNo));

            // e
            Cell uncollectedNumberCell = row.createCell(colNum++, CellType.STRING);
            String uncollectedNumber = map.getString("e");
            uncollectedNumberCell.setCellValue(uncollectedNumber);
            colWidth[colNum - 1] = Math.max(colWidth[colNum - 1], Strings.charLength(uncollectedNumber));

            // f
            Cell uncollectedDosageCell = row.createCell(colNum++, CellType.STRING);
            String uncollectedDosage = map.getString("f");
            uncollectedDosageCell.setCellValue(uncollectedDosage);
            colWidth[colNum - 1] = Math.max(colWidth[colNum - 1], Strings.charLength(uncollectedDosage));

            // g
            Cell uncollectedDosageCell1 = row.createCell(colNum++, CellType.STRING);
            String uncollectedDosage1 = map.getString("g");
            uncollectedDosageCell1.setCellValue(uncollectedDosage1);
            colWidth[colNum - 1] = Math.max(colWidth[colNum - 1], Strings.charLength(uncollectedDosage1));

            // h
            Cell uncollectedDosageCell2 = row.createCell(colNum++, CellType.STRING);
            String uncollectedDosage2 = map.getString("h");
            uncollectedDosageCell2.setCellValue(uncollectedDosage2);
            colWidth[colNum - 1] = Math.max(colWidth[colNum - 1], Strings.charLength(uncollectedDosage2));
        }
    }

写到本地

 private static void writeToLocal(String destination, InputStream input)
            throws IOException {
        int index = 0;
        byte[] bytes = new byte[1024];
        FileOutputStream downloadFile = new FileOutputStream(destination);
        while ((index = input.read(bytes)) > 0) {
            downloadFile.write(bytes, 0, index);
        }
        downloadFile.close();
        input.close();
    }

代码就这些,纯测试,有些命名很随意,到时候用的时候自己改

效果图

注意: 

本文读取文件是从本地读取的,部署后,需要把模板文件放在resource下,利用代码中

 // 此处linux和windows通用   /files/cq.xlsx 在resource目录下  视情况而定
           //  InputStream inputStream = this.getClass().getResourceAsStream("/files/cq.xlsx");

 来读取(本人框架非spring,spring自行解决),总之,获取到模板文件就是成功!!

如果想要设置单元格的样式比如字体垂直、水平居中等可以在  fillBodyData 中添加如下代码

 // 单元格样式
        XSSFCellStyle cellStyle = workbook.createCellStyle();
        // 设置单元格的水平对齐类型
        cellStyle.setAlignment(HorizontalAlignment.CENTER);
        // 设置单元格的垂直对齐类型
        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);

然后再循环体内每个cell设置style ,例如:

 Cell accountNumberCell = row.createCell(colNum++, CellType.STRING);
            String accountNumber = map.getString("comm");
            colWidth[colNum - 1] = Math.max(colWidth[colNum - 1], Strings.charLength(accountNumber));
            accountNumberCell.setCellValue(accountNumber);
// 设置style
            accountNumberCell.setCellStyle(cellStyle);

后续有更好的我会更新此贴,大家将就一下

续篇来了 ,基本上没区别就是poi导出excel的三种形式的简单说明和解释

java 利用poi根据excel模板导出数据(二)

来源:Stuild Stuil

物联沃分享整理
物联沃-IOTWORD物联网 » java 利用poi根据excel模板导出数据(一)

发表评论