当前位置: 首页 > news >正文

poi读取Excel模板并修改模板内容与动态的增加行

  有时候我们可能遇到相当复杂的excel,比如表头的合并等操作,一种简单的方式就是直接代码合并(浪费时间),另一种就是写好模板,动态的向模板中增加行和修改指定单元格数据。

1.一个简单的根据模板sheet动态修改

  原来的excel模板内容如下:

 

 现在的需求是动态的生成生成时间和生成人。并且在第五行开始的数据列表增加5列:

package cn.xm.exam.test;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

public class DynamicOperateExcelUtils {

    public static void main(String[] args) throws IOException {
        // 读取源文件
        FileInputStream fis = new FileInputStream("G:/test.xlsx");
        XSSFWorkbook workBook = new XSSFWorkbook(fis);

        // 进行模板的克隆(接下来的操作都是针对克隆后的sheet)
        XSSFSheet sheet = workBook.cloneSheet(0);
        workBook.setSheetName(0, "sheet-0"); // 给sheet命名

        // 读取指定cell的内容
        XSSFCell nameCell = sheet.getRow(1).getCell(0);
        XSSFCell nameCell2 = sheet.getRow(1).getCell(1);
        System.out.println(nameCell.getStringCellValue());
        System.out.println(nameCell2.getStringCellValue());

        // 替换单元格内容(注意获取的cell的下标是合并之前的下标)
        replaceCellValue(sheet.getRow(1).getCell(2), "xxxxx时间");
        replaceCellValue(sheet.getRow(2).getCell(2), "xxxxx人");

        // 动态插入数据-增加行
        List<Map<String, Object>> datas = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            Map data = new HashMap<>();
            data.put("name", "name" + i);
            data.put("age", "age" + i);
            data.put("sex", "sex" + i);
            datas.add(data);
        }
        // 插入行
        sheet.shiftRows(4, 4 + datas.size(), datas.size(), true, false);// 第1个参数是指要开始插入的行,第2个参数是结尾行数,第三个参数表示动态添加的行数
        for (int i = 0; i < datas.size(); i++) {
            XSSFRow creRow = sheet.createRow(4 + i);
            creRow.setRowStyle(sheet.getRow(4).getRowStyle());
            creRow.createCell(0).setCellValue(datas.get(i).get("name").toString());
            creRow.createCell(1).setCellValue(datas.get(i).get("age").toString());
            creRow.createCell(2).setCellValue(datas.get(i).get("sex").toString());
        }

        // 输出为一个新的Excel,也就是动态修改完之后的excel
        String fileName = "test" + System.currentTimeMillis() + ".xlsx";
        OutputStream out = new FileOutputStream("G:" + "/" + fileName);
        workBook.removeSheetAt(0); // 移除workbook中的模板sheet
        workBook.write(out);

        fis.close();
        out.flush();
        out.close();
    }

    /**
     * 替换单元格的内容,单元格的获取位置是合并单元格之前的位置,也就是下标都是合并之前的下表
     * 
     * @param cell
     *            单元格
     * @param value
     *            需要设置的值
     */
    public static void replaceCellValue(Cell cell, Object value) {
        String val = value != null ? String.valueOf(value) : "";
        cell.setCellValue(val);
    }
}

 结果:

 

   上面需要注意的是:在替换的时候获取cell的时候获取的是合并单元格之前的cell位置,在动态增加行的时候行的其实和结束都是包含在内的。

 

 2.  封装的一个完整的工具类:

  此工具类支持xls和xlsx格式(这也是一种常用的思想,用父类引用接受子类对象),完美的支持excel的操作。而且是单个sheet的模板替换以及追加内容。

  代码中依赖的工具包:Slf4j日志包,IOUtils工具包,commons-collections操作集合包。

package cn.xm.exam.utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections.MapUtils;
import org.apache.commons.io.IOUtils;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DynamicOperateExcelUtils {

    private static final Logger LOGGER = LoggerFactory.getLogger(DynamicOperateExcelUtils.class);

    private Workbook workBook;
    private Sheet sheet;

    public DynamicOperateExcelUtils(String fileFullPath, String sheetName) {
        // 解决版本问题,HSSFWorkbook是97-03版本的xls版本,XSSFWorkbook是07版本的xlsx
        try {
            workBook = new XSSFWorkbook(new FileInputStream(fileFullPath));
        } catch (Exception e) {
            try {
                workBook = new HSSFWorkbook(new FileInputStream(fileFullPath));
            } catch (Exception e1) {
                LOGGER.error("Excel格式不正确", e1);
                throw new RuntimeException(e1);
            }
        }

        // 进行模板的克隆(接下来的操作都是针对克隆后的sheet)
        sheet = workBook.cloneSheet(0);
        // 移除workbook中的模板sheet
        workBook.removeSheetAt(0);
        // 重命名克隆后的sheet
        workBook.setSheetName(0, sheetName != null ? sheetName : "sheet1");
    }

    /**
     * 读取cell的值
     * 
     * @param cell
     *            需要读取的cell
     * @param defaultValue
     *            默认值
     * @return
     */
    public String getCellStringValue(Cell cell, String defaultValue) {
        if (cell != null) {
            cell.setCellType(cell.CELL_TYPE_STRING);
            return cell.getStringCellValue();
        }

        return defaultValue;
    }

    /**
     * 替换单元格的内容,单元格的获取位置是合并单元格之前的位置,也就是下标都是合并之前的下表
     * 
     * @param cell
     *            单元格
     * @param value
     *            需要设置的值
     */
    public void replaceCellValue(Cell cell, Object value) {
        String val = value != null ? String.valueOf(value) : "";
        cell.setCellValue(val);
    }

    /**
     * 向sheet中添加行,后面的行会向后自动移动
     * 
     * @param startRowIndex
     *            起始行
     * @param datas
     *            数据
     * @param keys
     *            数据中Map对应的key
     */
    public void appendRows(int startRowIndex, List<Map<String, Object>> datas, String[] keys) {
        // 插入行
        sheet.shiftRows(startRowIndex, startRowIndex + datas.size(), datas.size(), true, false);// 第1个参数是指要开始插入的行,第2个参数是结尾行数,第三个参数表示动态添加的行数
        // 向插入的行中动态的填充数据
        for (int i = 0; i < datas.size(); i++) {
            Map<String, Object> data = datas.get(i);
            // 创建行
            Row row = sheet.createRow(startRowIndex + i);
            // 添加单元格
            Cell cell = null;
            for (int j = 0, length_2 = keys.length; j < length_2; j++) {
                String key = keys[j];
                String value = MapUtils.getString(data, key, "");
                cell = row.createCell(j);
                cell.setCellType(Cell.CELL_TYPE_STRING);
                cell.setCellValue(value);
            }
        }

        // 调整列宽
        autoResizeColumn(keys.length);
    }

    public void exportExcel(File file) {
        exportExcel(file.getAbsolutePath());
    }

    public void exportExcel(String fileFullPath) {
        OutputStream outputStream = null;
        try {
            outputStream = new FileOutputStream(fileFullPath);
            workBook.write(outputStream);
        } catch (IOException e) {
            LOGGER.error(" exportExcel error", e);
        } finally {
            IOUtils.closeQuietly(outputStream);
        }
    }

    private void autoResizeColumn(int colNumber) {
        // 如果是SXSSFSheet,需要调用trackAllColumnsForAutoSizing方法一次
        if (sheet instanceof SXSSFSheet) {
            SXSSFSheet tmpSheet = (SXSSFSheet) sheet;
            tmpSheet.trackAllColumnsForAutoSizing();
        }

        for (int i = 0; i < colNumber; i++) {
            sheet.autoSizeColumn(i);
        }
    }

    public Sheet getSheet() {
        return sheet;
    }

}

 

测试代码:

        public static void main(String[] args) throws IOException {
            DynamicOperateExcelUtils dynamicOperateExcelUtils = new DynamicOperateExcelUtils("G:/test.xls", null);
            // 读取指定cell的内容
            Cell nameCell = dynamicOperateExcelUtils.getSheet().getRow(1).getCell(0);
            System.out.println(nameCell.getStringCellValue());

            // 替换单元格内容(注意获取的cell的下标是合并之前的下标)
            dynamicOperateExcelUtils.replaceCellValue(dynamicOperateExcelUtils.getSheet().getRow(1).getCell(2), "xxxxx时间");
            dynamicOperateExcelUtils.replaceCellValue(dynamicOperateExcelUtils.getSheet().getRow(2).getCell(2), "xxxxx人");

            // 动态插入数据-增加行
            List<Map<String, Object>> datas = new ArrayList<>();
            for (int i = 0; i < 5; i++) {
                Map data = new HashMap<>();
                data.put("name", "name" + i);
                data.put("age", "age" + i);
                data.put("sex", "sex" + i);
                datas.add(data);
            }
            dynamicOperateExcelUtils.appendRows(4, datas, new String[] { "name", "age", "sex" });
            dynamicOperateExcelUtils.exportExcel(new File("G:/xxx.xlsx"));
    }

 

 

补充:有的POI版本如果第index行没有任何数据,直接getRow(index)的时候会报错,所以有可能需要先创建行。(这个问题对不同的POI版本情况不一样)

 

补充:有时候遇到合并的单元格采用上面自动调整列宽不生效,解决办法:

sheet.autoSizeColumn(i, true);

 

转载于:https://www.cnblogs.com/qlqwjy/p/10136578.html

相关文章:

  • Siri shortcuts 指北
  • day15生成器面试题和内置函数
  • idea debug
  • 关于XML文件
  • SQL自动流水号函数
  • NOI2018屠龙勇士(扩展CRT + splay(multiset))
  • 4 Redis 配置文件介绍
  • 定时任务Cron常用表达式与在线生成器
  • str()函数
  • Codeforces 1087C Connect Three (思维+模拟)
  • 网络图片转换到本地并转换成base64位
  • 最新最全 中文版Pycharm 2017安装教程 Python编译器安装(小白教程)
  • Spring事务管理要点总结
  • flask实现基于elasticsearch的关键词搜索建议
  • MySQL binlog group commit--commit stage
  • ES6指北【2】—— 箭头函数
  • [笔记] php常见简单功能及函数
  • 【个人向】《HTTP图解》阅后小结
  • AWS实战 - 利用IAM对S3做访问控制
  • docker python 配置
  • ES10 特性的完整指南
  • Hibernate最全面试题
  • JWT究竟是什么呢?
  • Laravel5.4 Queues队列学习
  • Node 版本管理
  • PHP的类修饰符与访问修饰符
  • Promise初体验
  • React-redux的原理以及使用
  • Redis提升并发能力 | 从0开始构建SpringCloud微服务(2)
  • Webpack入门之遇到的那些坑,系列示例Demo
  • 关于Flux,Vuex,Redux的思考
  • 京东美团研发面经
  • 前端之React实战:创建跨平台的项目架构
  • 强力优化Rancher k8s中国区的使用体验
  • 手机app有了短信验证码还有没必要有图片验证码?
  • 腾讯视频格式如何转换成mp4 将下载的qlv文件转换成mp4的方法
  • 运行时添加log4j2的appender
  • 最近的计划
  • 《天龙八部3D》Unity技术方案揭秘
  • const的用法,特别是用在函数前面与后面的区别
  • Nginx实现动静分离
  • ​LeetCode解法汇总2182. 构造限制重复的字符串
  • ​queue --- 一个同步的队列类​
  • #includecmath
  • #QT(一种朴素的计算器实现方法)
  • #调用传感器数据_Flink使用函数之监控传感器温度上升提醒
  • (echarts)echarts使用时重新加载数据之前的数据存留在图上的问题
  • (js)循环条件满足时终止循环
  • (M)unity2D敌人的创建、人物属性设置,遇敌掉血
  • (pojstep1.1.2)2654(直叙式模拟)
  • (八)光盘的挂载与解挂、挂载CentOS镜像、rpm安装软件详细学习笔记
  • (附源码)springboot学生选课系统 毕业设计 612555
  • (更新)A股上市公司华证ESG评级得分稳健性校验ESG得分年均值中位数(2009-2023年.12)
  • (九)One-Wire总线-DS18B20
  • (论文阅读40-45)图像描述1