有时候我们可能遇到相当复杂的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的模板替换以及追加内容、读取和设置单个cell的值。

  代码中依赖的工具包: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) {
this(fileFullPath, null);
} 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");
} public String getCellValue(int rowNum, int colNum) {
return getCellValue(rowNum, colNum, "");
} /**
* 根据行号列号获取值
*
* @param rowNum
* 行号
* @param colNum
* 列号
* @param defaultValue
* 默认值
* @return
*/
public String getCellValue(int rowNum, int colNum, String defaultValue) {
Row row = sheet.getRow(rowNum);
if (row == null) {
return defaultValue;
} Cell cell = row.getCell(colNum);
if (cell == null) {
return defaultValue;
} return getCellValue(cell, defaultValue);
} public String getCellValue(Cell cell) {
return getCellValue(cell, "");
} /**
* 读取cell的值
*
* @param cell
* 需要读取的cell
* @param defaultValue
* 默认值
* @return
*/
public String getCellValue(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);
} /**
* 根据行号,列号进行替换
*
* @param rowNum
* 行号
* @param colNum
* 列号
* @param value
* 值
*/
public void replaceCellValue(int rowNum, int colNum, Object value) {
Row row = sheet.getRow(rowNum);
if (row == null) {
return;
} Cell cell = row.getCell(colNum);
if (cell == null) {
return;
} replaceCellValue(cell, value);
} /**
* 向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, true);
}
} public Sheet getSheet() {
return sheet;
} }

测试:

原来excel: myExcel.xlsx

代码:

    public static void main(String[] args) throws IOException {
DynamicOperateExcelUtils dynamicOperateExcelUtils = new DynamicOperateExcelUtils("F:/myExcel.xlsx"); // 读取内容
String cellValue = dynamicOperateExcelUtils.getCellValue(1, 1);
System.out.println(cellValue); // 替换单元格内容(注意获取的cell的下标是合并之前的下标)
dynamicOperateExcelUtils.replaceCellValue(1, 1, "updated"); // 动态插入数据-增加行
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("F:/myExcel2.xlsx"));
}

结果:

什么鬼

myExcel2.xlsx

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

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

sheet.autoSizeColumn(i, true);

poi读取Excel模板并修改模板内容与动态的增加行的更多相关文章

  1. poi读取excel模板,填充内容并导出,支持导出2007支持公式自动计算

    /** * 版权所有(C) 2016 * @author www.xiongge.club * @date 2016-12-7 上午10:03:29 */ package xlsx; /** * @C ...

  2. POI读取Excel内容格式化

    在用POI读取Excel内容时,经常会遇到数据格式化的问题. 比如:数字12365会变为12365.0;字符串数字123也会变为123.0,甚至会被变为科学计数法.另外日期格式化也是一个头疼的问题.其 ...

  3. 项目一:第四天 1、快递员的条件分页查询-noSession,条件查询 2、快递员删除(逻辑删除) 3、基于Apache POI实现批量导入区域数据 a)Jquery OCUpload上传文件插件使用 b)Apache POI读取excel文件数据

    1. 快递员的条件分页查询-noSession,条件查询 2. 快递员删除(逻辑删除) 3. 基于Apache POI实现批量导入区域数据 a) Jquery OCUpload上传文件插件使用 b) ...

  4. 使用jxl,poi读取excel文件

    作用:在java后台添加一个方法,读取导入的excel内容,根据需要返回相应的sql语句,以完成对临时表的插入操作. 使用jxl读取excel文件 package com.sixthf.bi.sapp ...

  5. java用poi读取Excel表格中的数据

    Java读写Excel的包是Apache POI(项目地址:http://poi.apache.org/),因此需要先获取POI的jar包,本实验使用的是POI 3.9稳定版.Apache POI 代 ...

  6. python读取excel中单元格的内容返回的5种类型

    (1) 读取单个sheetname的内容. 此部分转自:https://www.cnblogs.com/xxiong1031/p/7069006.html python读取excel中单元格的内容返回 ...

  7. Java开发小技巧(六):使用Apache POI读取Excel

    前言 在数据仓库中,ETL最基础的步骤就是从数据源抽取所需的数据,这里所说的数据源并非仅仅是指数据库,还包括excel.csv.xml等各种类型的数据接口文件,而这些文件中的数据不一定是结构化存储的, ...

  8. Java之POI读取Excel的Package should contain a content type part [M1.13]] with root cause异常问题解决

    Java之POI读取Excel的Package should contain a content type part [M1.13]] with root cause异常问题解决 引言: 在Java中 ...

  9. POI读取Excel数据

    POI读取Excel表格数据 * {所需相关jar下载: * commons-collections4-4.4.jar * commons-compress-1.19.jar * poi-4.1.1. ...

随机推荐

  1. 深入理解之css中的border属性

    1. border-width:不支持不百分比 1)受本身的使用场景决定. 例子:左边为手机,右边为显示器,但是他们边框的宽度是差不多的,不会因为设备大就让边框宽度变大. 2. border-widt ...

  2. 10款 Mac 系统优化清理工具软件推荐和下载

    本文内容及图片来源[风云社区 SCOEE] 在Windows上有各种安全卫士.系统助手等系统优化和清理工具,比如360安全卫士.腾讯安全管家等,同样MacOS系统也有很多好用的系统优化清理工具,体验比 ...

  3. pt-online-schema-change 测试使用-包含生成测试数据

    pt-online-schema-change 测试使用-包含生成测试数据 # 参考网址: https://www.2cto.com/database/201703/618280.html 一.简要描 ...

  4. Python递归_打印节点信息

    Python递归_打印节点信息 递归特性:1.必须由一个明确的结束条件2.每次进入更深一层递归时,问题规模相比上一次递归都应该有所减少3.递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用时 ...

  5. mac/Linux源码安装TensorFlow

    因为用pip命令直接下载安装会链接到google,导致打不开,比如使用pip install tensorflow碰到如下的问题.因此在本文中,主要介绍了如何通过源码进行TensorFlow的安装 $ ...

  6. HTTP Method小结

    GET 从指定的url上获取内容 POST 提交body中的内容给服务器中指定的url中,属于非幂等的(non-idempotent)请求 HEAD 从指定的url上获取header内容(类似Get方 ...

  7. layui(六)——upload组件常见用法总结

    layui中提供了非常简单的文件上传组件,这里写了一个上传图片的栗子,上传成功后返回图片在服务器的路径,并设置为页面中img的src属性.因为上传十分简单,没什么可说的,就直接上代码了. html代码 ...

  8. Idea构建maven项目

    Idea构建maven项目: 步骤一: 步骤二: 自动导入Maven项目: 步骤三:增加web 二:搭建spring项目结构: 结构图: 网上都是一大堆的:自己也可以去搜:ssm  pom.xml  ...

  9. 错误 1 “Entities.PlanPrjEntity.PlanPrjs”不可访问,因为它受保护级别限制

    本人第一次是用List做父类,写了一个类PlanPrjs,如下: class PlanPrj { public int ID { get; set; } public string Name { ge ...

  10. Mac OS X 启用超级用户 sudo -s 获得系统权限 Mac终端命令

    为了防止误操作破坏系统,用户状态下时没有权限操作系统重要文件, 所以先要取得root权限:“sudo -s” 详见:https://www.jianshu.com/p/138b98e662ed