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

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

  原来的excel模板内容如下:

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

  1. package cn.xm.exam.test;
  2.  
  3. import java.io.FileInputStream;
  4. import java.io.FileOutputStream;
  5. import java.io.IOException;
  6. import java.io.OutputStream;
  7. import java.util.ArrayList;
  8. import java.util.HashMap;
  9. import java.util.List;
  10. import java.util.Map;
  11.  
  12. import org.apache.poi.ss.usermodel.Cell;
  13. import org.apache.poi.xssf.usermodel.XSSFCell;
  14. import org.apache.poi.xssf.usermodel.XSSFRow;
  15. import org.apache.poi.xssf.usermodel.XSSFSheet;
  16. import org.apache.poi.xssf.usermodel.XSSFWorkbook;
  17.  
  18. public class DynamicOperateExcelUtils {
  19.  
  20. public static void main(String[] args) throws IOException {
  21. // 读取源文件
  22. FileInputStream fis = new FileInputStream("G:/test.xlsx");
  23. XSSFWorkbook workBook = new XSSFWorkbook(fis);
  24.  
  25. // 进行模板的克隆(接下来的操作都是针对克隆后的sheet)
  26. XSSFSheet sheet = workBook.cloneSheet(0);
  27. workBook.setSheetName(0, "sheet-0"); // 给sheet命名
  28.  
  29. // 读取指定cell的内容
  30. XSSFCell nameCell = sheet.getRow(1).getCell(0);
  31. XSSFCell nameCell2 = sheet.getRow(1).getCell(1);
  32. System.out.println(nameCell.getStringCellValue());
  33. System.out.println(nameCell2.getStringCellValue());
  34.  
  35. // 替换单元格内容(注意获取的cell的下标是合并之前的下标)
  36. replaceCellValue(sheet.getRow(1).getCell(2), "xxxxx时间");
  37. replaceCellValue(sheet.getRow(2).getCell(2), "xxxxx人");
  38.  
  39. // 动态插入数据-增加行
  40. List<Map<String, Object>> datas = new ArrayList<>();
  41. for (int i = 0; i < 5; i++) {
  42. Map data = new HashMap<>();
  43. data.put("name", "name" + i);
  44. data.put("age", "age" + i);
  45. data.put("sex", "sex" + i);
  46. datas.add(data);
  47. }
  48. // 插入行
  49. sheet.shiftRows(4, 4 + datas.size(), datas.size(), true, false);// 第1个参数是指要开始插入的行,第2个参数是结尾行数,第三个参数表示动态添加的行数
  50. for (int i = 0; i < datas.size(); i++) {
  51. XSSFRow creRow = sheet.createRow(4 + i);
  52. creRow.setRowStyle(sheet.getRow(4).getRowStyle());
  53. creRow.createCell(0).setCellValue(datas.get(i).get("name").toString());
  54. creRow.createCell(1).setCellValue(datas.get(i).get("age").toString());
  55. creRow.createCell(2).setCellValue(datas.get(i).get("sex").toString());
  56. }
  57.  
  58. // 输出为一个新的Excel,也就是动态修改完之后的excel
  59. String fileName = "test" + System.currentTimeMillis() + ".xlsx";
  60. OutputStream out = new FileOutputStream("G:" + "/" + fileName);
  61. workBook.removeSheetAt(0); // 移除workbook中的模板sheet
  62. workBook.write(out);
  63.  
  64. fis.close();
  65. out.flush();
  66. out.close();
  67. }
  68.  
  69. /**
  70. * 替换单元格的内容,单元格的获取位置是合并单元格之前的位置,也就是下标都是合并之前的下表
  71. *
  72. * @param cell
  73. * 单元格
  74. * @param value
  75. * 需要设置的值
  76. */
  77. public static void replaceCellValue(Cell cell, Object value) {
  78. String val = value != null ? String.valueOf(value) : "";
  79. cell.setCellValue(val);
  80. }
  81. }

结果:

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

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

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

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

  1. package cn.xm.exam.utils;
  2.  
  3. import java.io.File;
  4. import java.io.FileInputStream;
  5. import java.io.FileOutputStream;
  6. import java.io.IOException;
  7. import java.io.OutputStream;
  8. import java.util.List;
  9. import java.util.Map;
  10.  
  11. import org.apache.commons.collections.MapUtils;
  12. import org.apache.commons.io.IOUtils;
  13. import org.apache.poi.hssf.usermodel.HSSFWorkbook;
  14. import org.apache.poi.ss.usermodel.Cell;
  15. import org.apache.poi.ss.usermodel.Row;
  16. import org.apache.poi.ss.usermodel.Sheet;
  17. import org.apache.poi.ss.usermodel.Workbook;
  18. import org.apache.poi.xssf.streaming.SXSSFSheet;
  19. import org.apache.poi.xssf.usermodel.XSSFWorkbook;
  20. import org.slf4j.Logger;
  21. import org.slf4j.LoggerFactory;
  22.  
  23. public class DynamicOperateExcelUtils {
  24.  
  25. private static final Logger LOGGER = LoggerFactory.getLogger(DynamicOperateExcelUtils.class);
  26.  
  27. private Workbook workBook;
  28. private Sheet sheet;
  29.  
  30. public DynamicOperateExcelUtils(String fileFullPath) {
  31. this(fileFullPath, null);
  32. }
  33.  
  34. public DynamicOperateExcelUtils(String fileFullPath, String sheetName) {
  35. // 解决版本问题,HSSFWorkbook是97-03版本的xls版本,XSSFWorkbook是07版本的xlsx
  36. try {
  37. workBook = new XSSFWorkbook(new FileInputStream(fileFullPath));
  38. } catch (Exception e) {
  39. try {
  40. workBook = new HSSFWorkbook(new FileInputStream(fileFullPath));
  41. } catch (Exception e1) {
  42. LOGGER.error("Excel格式不正确", e1);
  43. throw new RuntimeException(e1);
  44. }
  45. }
  46.  
  47. // 进行模板的克隆(接下来的操作都是针对克隆后的sheet)
  48. sheet = workBook.cloneSheet(0);
  49. // 移除workbook中的模板sheet
  50. workBook.removeSheetAt(0);
  51. // 重命名克隆后的sheet
  52. workBook.setSheetName(0, sheetName != null ? sheetName : "sheet1");
  53. }
  54.  
  55. public String getCellValue(int rowNum, int colNum) {
  56. return getCellValue(rowNum, colNum, "");
  57. }
  58.  
  59. /**
  60. * 根据行号列号获取值
  61. *
  62. * @param rowNum
  63. * 行号
  64. * @param colNum
  65. * 列号
  66. * @param defaultValue
  67. * 默认值
  68. * @return
  69. */
  70. public String getCellValue(int rowNum, int colNum, String defaultValue) {
  71. Row row = sheet.getRow(rowNum);
  72. if (row == null) {
  73. return defaultValue;
  74. }
  75.  
  76. Cell cell = row.getCell(colNum);
  77. if (cell == null) {
  78. return defaultValue;
  79. }
  80.  
  81. return getCellValue(cell, defaultValue);
  82. }
  83.  
  84. public String getCellValue(Cell cell) {
  85. return getCellValue(cell, "");
  86. }
  87.  
  88. /**
  89. * 读取cell的值
  90. *
  91. * @param cell
  92. * 需要读取的cell
  93. * @param defaultValue
  94. * 默认值
  95. * @return
  96. */
  97. public String getCellValue(Cell cell, String defaultValue) {
  98. if (cell != null) {
  99. cell.setCellType(cell.CELL_TYPE_STRING);
  100. return cell.getStringCellValue();
  101. }
  102.  
  103. return defaultValue;
  104. }
  105.  
  106. /**
  107. * 替换单元格的内容,单元格的获取位置是合并单元格之前的位置,也就是下标都是合并之前的下表
  108. *
  109. * @param cell
  110. * 单元格
  111. * @param value
  112. * 需要设置的值
  113. */
  114. public void replaceCellValue(Cell cell, Object value) {
  115. String val = value != null ? String.valueOf(value) : "";
  116. cell.setCellValue(val);
  117. }
  118.  
  119. /**
  120. * 根据行号,列号进行替换
  121. *
  122. * @param rowNum
  123. * 行号
  124. * @param colNum
  125. * 列号
  126. * @param value
  127. * 值
  128. */
  129. public void replaceCellValue(int rowNum, int colNum, Object value) {
  130. Row row = sheet.getRow(rowNum);
  131. if (row == null) {
  132. return;
  133. }
  134.  
  135. Cell cell = row.getCell(colNum);
  136. if (cell == null) {
  137. return;
  138. }
  139.  
  140. replaceCellValue(cell, value);
  141. }
  142.  
  143. /**
  144. * 向sheet中添加行,后面的行会向后自动移动
  145. *
  146. * @param startRowIndex
  147. * 起始行
  148. * @param datas
  149. * 数据
  150. * @param keys
  151. * 数据中Map对应的key
  152. */
  153. public void appendRows(int startRowIndex, List<Map<String, Object>> datas, String[] keys) {
  154. // 插入行
  155. sheet.shiftRows(startRowIndex, startRowIndex + datas.size(), datas.size(), true, false);// 第1个参数是指要开始插入的行,第2个参数是结尾行数,第三个参数表示动态添加的行数
  156. // 向插入的行中动态的填充数据
  157. for (int i = 0; i < datas.size(); i++) {
  158. Map<String, Object> data = datas.get(i);
  159. // 创建行
  160. Row row = sheet.createRow(startRowIndex + i);
  161. // 添加单元格
  162. Cell cell = null;
  163. for (int j = 0, length_2 = keys.length; j < length_2; j++) {
  164. String key = keys[j];
  165. String value = MapUtils.getString(data, key, "");
  166. cell = row.createCell(j);
  167. cell.setCellType(Cell.CELL_TYPE_STRING);
  168. cell.setCellValue(value);
  169. }
  170. }
  171.  
  172. // 调整列宽
  173. autoResizeColumn(keys.length);
  174. }
  175.  
  176. public void exportExcel(File file) {
  177. exportExcel(file.getAbsolutePath());
  178. }
  179.  
  180. public void exportExcel(String fileFullPath) {
  181. OutputStream outputStream = null;
  182. try {
  183. outputStream = new FileOutputStream(fileFullPath);
  184. workBook.write(outputStream);
  185. } catch (IOException e) {
  186. LOGGER.error(" exportExcel error", e);
  187. } finally {
  188. IOUtils.closeQuietly(outputStream);
  189. }
  190. }
  191.  
  192. private void autoResizeColumn(int colNumber) {
  193. // 如果是SXSSFSheet,需要调用trackAllColumnsForAutoSizing方法一次
  194. if (sheet instanceof SXSSFSheet) {
  195. SXSSFSheet tmpSheet = (SXSSFSheet) sheet;
  196. tmpSheet.trackAllColumnsForAutoSizing();
  197. }
  198.  
  199. for (int i = 0; i < colNumber; i++) {
  200. sheet.autoSizeColumn(i, true);
  201. }
  202. }
  203.  
  204. public Sheet getSheet() {
  205. return sheet;
  206. }
  207.  
  208. }

测试:

原来excel: myExcel.xlsx

代码:

  1. public static void main(String[] args) throws IOException {
  2. DynamicOperateExcelUtils dynamicOperateExcelUtils = new DynamicOperateExcelUtils("F:/myExcel.xlsx");
  3.  
  4. // 读取内容
  5. String cellValue = dynamicOperateExcelUtils.getCellValue(1, 1);
  6. System.out.println(cellValue);
  7.  
  8. // 替换单元格内容(注意获取的cell的下标是合并之前的下标)
  9. dynamicOperateExcelUtils.replaceCellValue(1, 1, "updated");
  10.  
  11. // 动态插入数据-增加行
  12. List<Map<String, Object>> datas = new ArrayList<>();
  13. for (int i = 0; i < 5; i++) {
  14. Map data = new HashMap<>();
  15. data.put("name", "name" + i);
  16. data.put("age", "age" + i);
  17. data.put("sex", "sex" + i);
  18. datas.add(data);
  19. }
  20. dynamicOperateExcelUtils.appendRows(4, datas, new String[] { "name", "age", "sex" });
  21.  
  22. dynamicOperateExcelUtils.exportExcel(new File("F:/myExcel2.xlsx"));
  23. }

结果:

什么鬼

myExcel2.xlsx

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

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

  1. 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. SSH整合redis和MongoDB错误笔记

    由于毕设中做的是图片搜索网站,选择前端框用SSH,因为之间接触过SSH框架,略有了解,但没有深究,现在在整合redis和mongodb的过程中遇到很多错误,也是十分痛苦,只能通过百度和一步步尝试着解决 ...

  2. 对manacher的一点感性理解

    因为总是忘掉板子所以这里贴一下我个人对\(manacher\)的感性理解. 可能不够严谨求轻喷\(QwQ\) char ch = getchar (); s[0] = s[1] = '#'; whil ...

  3. Altium Designer 17 ------ 多层板设计

    Pullback:在内电层边缘设置一个去铜边界,以保证内电层边界距离PCB边缘有一个安全间距.

  4. maven直接饮用jar包的写法

    <dependency> <groupId>sample</groupId> <artifactId>com.sample</artifactId ...

  5. 图论分支-Tarjan初步-割点和割边

    所谓割点(顶)割边,我们引进一个概念 割点:删掉它之后(删掉所有跟它相连的边),图必然会分裂成两个或两个以上的子图. 割边(桥):删掉一条边后,图必然会分裂成两个或两个以上的子图,又称桥. 这样大家就 ...

  6. MySQL权限授权认证详解

    MySQL权限授权认证详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.MySQL权限系统介绍1>.权限系统的作用是授予来自某个主机的某个用户可以查询.插入.修改.删除 ...

  7. WebAPI性能优化之压缩解压

    有时候为了提升WebAPI的性能,减少响应时间,我们会使用压缩和解压,而现在大多数客户端浏览器都提供了内置的解压支持.在WebAPI请求的资源越大时,使用压缩对性能提升的效果越明显,而当请求的资源很小 ...

  8. vue实现简单日历

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  9. 056、macvlan网络结构分析(2019-03-25 周一)

    参考https://www.cnblogs.com/CloudMan6/p/7383919.html   macvlan不依赖linux bridge   brctl show 可以确认没有创建新的b ...

  10. IScroll在某些手机浏览器上不能滑动和卡顿解决办法

    1.不能滑动,增加一句 if (scroll != null) scroll.refresh();2.卡顿,增加 <script>window.PointerEvent = undefin ...