场景:项目中遇到有需要导出Excel的需求,并且是多行标题且有合并单元格的,参考网上的文章,加上自己的理解,封装成了可自由扩展的导出工具

先上效果,再贴代码:

调用工具类进行导出:

  1. public static void main(String[] args) throws Exception {
  2. FileOutputStream fos=new FileOutputStream("D:\\test.xls");
  3.  
  4. String[] head0 = new String[]{"周期", "时间范围", "付款", "付款", "站外推广", "站外推广", "销售", "销售", "退款", "退款", "宝贝成本", "净销售", "净销售", "统计汇总", "统计汇总"};
  5. String[] head1 = new String[]{" ", " ", "销量", "金额", "销量", "金额", "销量", "金额", "退款金额", "退回宝贝成本", "", "销量", "金额", "宝贝利润", "利润率"};
  6. //对应excel中的行和列,("开始行,结束行,开始列,结束列")
  7. String[] headnum0 = new String[]{"0,1,0,0", "0,1,1,1,", "0,0,2,3", "0,0,4,5", "0,0,6,7", "0,0,8,9", "0,1,10,10", "0,0,11,12", "0,0,13,14"};
  8. List<String> attrList = Lists.newArrayList("reportDate", "dateRange", "saleNum", "payFee", "specialSaleNum", "specialSaleFee", "totalSaleNum", "saleFee", "refundFee",
  9. "refundCost", "costFee", "retaSaleNum", "retaProfitFee", "profitFee", "profitPercent");
  10.  
  11. //组装头部Map
  12. Map<Integer, Map<String, String[]>> headMap = Maps.newHashMap();
  13. Map<String, String[]> head0InfoMap = Maps.newHashMap();
  14. head0InfoMap.put(ExcelUtil.HEADNAME, head0);
  15. head0InfoMap.put(ExcelUtil.HEADNUM, headnum0);
  16.  
  17. Map<String, String[]> head1InfoMap = Maps.newHashMap();
  18. head1InfoMap.put(ExcelUtil.HEADNAME, head1);
  19.  
  20. headMap.put(0, head0InfoMap);
  21. headMap.put(1, head1InfoMap);
  22.  
  23. //数据列表
  24. List<ItemProfitExcelVo> itemProfitExcelVoList = Lists.newArrayList();
  25. int i =10;
  26. while (i<20){
  27. i++;
  28. ItemProfitExcelVo itemProfitExcelVo = new ItemProfitExcelVo();
  29. itemProfitExcelVo.setReportDate("2019-06-"+i);
  30. itemProfitExcelVo.setDateRange("2019-06-"+i);
  31. itemProfitExcelVo.setPayFee(i+"元");
  32. itemProfitExcelVo.setSaleNum(i+"个");
  33. itemProfitExcelVoList.add(itemProfitExcelVo);
  34. }
  35.  
  36. Workbook workbook = ExcelUtil.exportMergeXls(sheetName, itemProfitExcelVoList, attrList, headMap, ItemProfitExcelVo.class);
  37. workbook.write(fos);
  38. fos.close();
  39. }

工具类ExcelUtil:

  1. package com.verse.hades.web.stats.util;
  2.  
  3. import com.google.common.base.Predicates;
  4. import com.verse.hades.core.constant.ResultCodeConstant;
  5. import com.verse.hades.core.exception.BusinessException;
  6. import com.verse.hades.excel.annotaion.ExcelNum;
  7. import com.verse.hades.excel.annotaion.ExcelPercent;
  8. import com.verse.hades.excel.annotaion.ExcelUnit;
  9. import com.verse.hades.utils.LocalDateUtil;
  10. import com.verse.hades.utils.MoneyUtil;
  11. import com.verse.hades.utils.ReflectionHelper;
  12. import org.apache.poi.ss.usermodel.*;
  13. import org.apache.poi.ss.util.CellRangeAddress;
  14. import org.apache.poi.xssf.usermodel.XSSFWorkbook;
  15. import org.reflections.ReflectionUtils;
  16.  
  17. import java.lang.reflect.Field;
  18. import java.lang.reflect.Method;
  19. import java.lang.reflect.Modifier;
  20. import java.time.LocalDate;
  21. import java.time.LocalDateTime;
  22. import java.util.*;
  23.  
  24. public class ExcelUtil {
  25. public static final String HEADNAME = "headName"; //列名 key的值
  26.  
  27. public static final String HEADNUM = "headNum"; //合并单元格下标 key的值
  28.  
  29. /**
  30. * @param: sheetName
  31. * @param: objList 导出的数据对象列表
  32. * @param: attr 导出数据对象对应的字段
  33. * @param: headMap 头部列表名称数组与合并单元格数组的Map
  34. * @param: clazz 对象类
  35. * @Return: org.apache.poi.ss.usermodel.Workbook
  36. * @Decription:
  37. * @Modify:
  38. */
  39. public static <T> Workbook exportMergeXls(String sheetName, List<T> objList, List<String> attr, Map<Integer/*行标*/,
  40. Map<String/*头部列表key的名字*/, String[]/*对应的值*/>> headMap, Class<T> clazz) {
  41. //String[] head0, String[] headnum0, String[] head1, String[] headnum1,
  42. Workbook workbook = new XSSFWorkbook();
  43. Sheet sheet = workbook.createSheet(sheetName);
  44.  
  45. //头部单元格样式
  46. CellStyle cellStyle = workbook.createCellStyle();
  47. setAlign(cellStyle, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
  48. setBorder(cellStyle, BorderStyle.THIN, IndexedColors.BLACK);
  49. setColor(cellStyle, IndexedColors.GREY_25_PERCENT.index, FillPatternType.SOLID_FOREGROUND);
  50.  
  51. Font font = workbook.createFont();
  52. font.setFontName("Arial");
  53. font.setFontHeightInPoints((short) 10);//设置字体大小
  54. cellStyle.setFont(font);
  55.  
  56. Set<Integer> headKeySet = headMap.keySet();
  57. if (headKeySet.size() > 0) {
  58. headKeySet.stream().sorted();
  59. for (Integer rowNum : headKeySet) {
  60. Map<String, String[]> rowInfo = headMap.get(rowNum);
  61. String[] headName = rowInfo.get(HEADNAME);
  62. String[] headNum = rowInfo.get(HEADNUM);
  63.  
  64. if (null != headName && headName.length > 0) {
  65. Row row1 = sheet.createRow(rowNum);
  66. for (int i = 0; i < headName.length; i++) {
  67. Cell cell = row1.createCell(i);
  68. cell.setCellValue(headName[i]);
  69. cell.setCellStyle(cellStyle);
  70. }
  71. }
  72.  
  73. if (null != headNum && headNum.length > 0) {
  74. //动态合并单元格
  75. for (String aHeadNum : headNum) {
  76. String[] temp = aHeadNum.split(",");
  77. Integer startrow = Integer.parseInt(temp[0]);
  78. Integer overrow = Integer.parseInt(temp[1]);
  79. Integer startcol = Integer.parseInt(temp[2]);
  80. Integer overcol = Integer.parseInt(temp[3]);
  81. CellRangeAddress cra = new CellRangeAddress(startrow, overrow, startcol, overcol);
  82. sheet.addMergedRegion(cra);
  83. }
  84. }
  85. }
  86. }
  87.  
  88. return insertData(workbook, sheet, objList, clazz, attr, headKeySet.size(), attr.size());
  89. }
  90.  
  91. /**
  92. * @param: workbook
  93. * @param: sheet
  94. * @param: objList 对象列表
  95. * @param: clazz 对象类
  96. * @param: attr 对象的字段名数组
  97. * @param: startRow 数据开始的行数
  98. * @param: colNum 总共的列数
  99. * @Return: org.apache.poi.ss.usermodel.Workbook
  100. * @Decription: 插入数据
  101. * @Modify:
  102. */
  103. private static <T> Workbook insertData(Workbook workbook, Sheet sheet, List<T> objList, Class<T> clazz, List<String> attr, int startRow, int colNum) {
  104. CellStyle cellStyle = workbook.createCellStyle();
  105. setAlign(cellStyle, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
  106. setBorder(cellStyle, BorderStyle.THIN, IndexedColors.BLACK);
  107. Font font = workbook.createFont();
  108. font.setFontName("Arial");
  109. font.setFontHeightInPoints((short) 10);//设置字体大小
  110. cellStyle.setFont(font);
  111.  
  112. List<List<Object>> lists = getValueList(objList, clazz, attr);
  113.  
  114. for (List<Object> list : lists) {
  115. Row row = sheet.createRow(startRow++);
  116. for (int col = 0; col < colNum; col++) {
  117. Cell cell = row.createCell(col);
  118.  
  119. //设置单元格换行
  120. cell.setCellStyle(cellStyle);
  121.  
  122. writeCell(workbook, list.get(col), cell);
  123. }
  124. }
  125. return workbook;
  126. }
  127.  
  128. /**
  129. * @param: workbook
  130. * @param: obj
  131. * @param: cell
  132. * @Return: void
  133. * @Decription: 将数据写入单元格
  134. * @Modify:
  135. */
  136. private static void writeCell(Workbook workbook, Object obj, Cell cell) {
  137. if (obj == null) {
  138. cell.setCellValue("");
  139. } else if (obj instanceof String) {
  140. cell.setCellValue((String) obj);
  141. } else if (obj instanceof Character) {
  142. cell.setCellValue((Character) obj);
  143. } else if (obj instanceof Long) {
  144. cell.setCellValue((Long) obj);
  145. } else if (obj instanceof Integer) {
  146. cell.setCellValue((Integer) obj);
  147. } else if (obj instanceof Short) {
  148. cell.setCellValue((Short) obj);
  149. } else if (obj instanceof Byte) {
  150. cell.setCellValue((Byte) obj);
  151. } else if (obj instanceof Float) {
  152. cell.setCellValue((Float) obj);
  153. } else if (obj instanceof Double) {
  154. cell.setCellValue((Double) obj);
  155. } else if (obj instanceof Boolean) {
  156. cell.setCellValue((Boolean) obj);
  157. } else if (obj instanceof Date) {
  158. CellStyle cellStyle = workbook.createCellStyle();
  159. CreationHelper createHelper = workbook.getCreationHelper();
  160. cellStyle.setDataFormat(
  161. createHelper.createDataFormat().getFormat("yyyy/m/d h:mm:ss"));
  162. cell.setCellValue((Date) obj);
  163. cell.setCellStyle(cellStyle);
  164. } else if (obj instanceof LocalDate) {
  165. cell.setCellValue(LocalDateUtil.getDateAsString((LocalDate) obj));
  166. } else if (obj instanceof LocalDateTime) {
  167. cell.setCellValue(LocalDateUtil.getDateTimeAsString((LocalDateTime) obj));
  168. }
  169. }
  170.  
  171. /**
  172. * @param: objList
  173. * @param: clazz
  174. * @param: attr
  175. * @Return: java.util.List<java.util.List<java.lang.Object>>
  176. * @Decription: 获取对象对应的字段的值
  177. * @Modify:
  178. */
  179. private static <T> List<List<Object>> getValueList(List<T> objList, Class<T> clazz, List<String> attr) {
  180. Set<Method> methods = ReflectionUtils.getAllMethods(clazz, Predicates.and(ReflectionUtils.withModifier(Modifier.PUBLIC),
  181. ReflectionUtils.withPrefix("get")));
  182. Map<String, Object> annotationMap = getAnnotaions(clazz);
  183.  
  184. Map<String, Method> methodMap = new HashMap<>();
  185. for (Method method : methods) {
  186. String attrName = ReflectionHelper.getAttrNameFromMethod(method.getName());
  187. if (attr.contains(attrName)) {
  188. methodMap.put(attrName, method);
  189. }
  190. }
  191.  
  192. if (methodMap.keySet().size() != attr.size()) {
  193. throw new BusinessException(ResultCodeConstant.ERROR_SERVICE_CODE, "excel格式错误,列表和数据不一致,处理失败");
  194. }
  195.  
  196. List<List<Object>> res = new ArrayList<>();
  197. for (T obj : objList) {
  198. List<Object> list = new ArrayList<>();
  199. for (String name : attr) {
  200. Method method = methodMap.get(name);
  201. list.add(wrapperValue(method, name, annotationMap.get(name), obj));
  202. }
  203. res.add(list);
  204. }
  205. return res;
  206. }
  207.  
  208. private static Object wrapperValue(Method method, String name, Object annotation, Object obj) {
  209. String hasConvert = "";
  210. Object value;
  211. try {
  212. value = method.invoke(obj);
  213. } catch (Exception e) {
  214. throw new BusinessException(ResultCodeConstant.ERROR_SERVICE_CODE, "excel格式错误,不能获取对应列的数据|" + name);
  215. }
  216.  
  217. if (value != null) {
  218. if (annotation instanceof ExcelPercent) {
  219. ExcelPercent excelPercent = (ExcelPercent) annotation;
  220. hasConvert = excelPercent.pre() + MoneyUtil.convertCentToString(getLongValue(value, name), 2) + "%";
  221. } else if (annotation instanceof ExcelNum) {
  222. ExcelNum excelNum = (ExcelNum) annotation;
  223. hasConvert = excelNum.pre() + MoneyUtil.convertCentToString(getLongValue(value, name), 2) + excelNum.unit();
  224. } else if (annotation instanceof ExcelUnit) {
  225. ExcelUnit excelUnit = (ExcelUnit) annotation;
  226. hasConvert = excelUnit.pre() + value + excelUnit.value();
  227. }
  228. }
  229.  
  230. if (hasConvert.equals("")) {
  231. return value;
  232. } else {
  233. return hasConvert;
  234. }
  235. }
  236.  
  237. private static Long getLongValue(Object value, String name) {
  238. Long conver2Long = 0L;
  239. if (value instanceof Integer) {
  240. conver2Long = ((Integer) value).longValue();
  241. } else if (value instanceof Long) {
  242. conver2Long = (Long) value;
  243. } else {
  244. throw new BusinessException(ResultCodeConstant.ERROR_SERVICE_CODE, "excel格式错误,当前类型无法转换" + name);
  245. }
  246. return conver2Long;
  247. }
  248.  
  249. private static Map<String, Object> getAnnotaions(Class c) {
  250. Map<String, Object> annotationMap = new HashMap<>();
  251. Field[] fields = c.getDeclaredFields();
  252. for (Field field : fields) {
  253. if (field.getAnnotation(ExcelPercent.class) != null) {
  254. annotationMap.put(field.getName(), field.getAnnotation(ExcelPercent.class));
  255. } else if (field.getAnnotation(ExcelNum.class) != null) {
  256. annotationMap.put(field.getName(), field.getAnnotation(ExcelNum.class));
  257. } else if (field.getAnnotation(ExcelUnit.class) != null) {
  258. annotationMap.put(field.getName(), field.getAnnotation(ExcelUnit.class));
  259. }
  260. }
  261. return annotationMap;
  262. }
  263.  
  264. private static void setAlign(CellStyle cellStyle, HorizontalAlignment halign, VerticalAlignment valign) {
  265. cellStyle.setAlignment(halign);
  266. cellStyle.setVerticalAlignment(valign);
  267. }
  268.  
  269. private static void setBorder(CellStyle cellStyle, BorderStyle borderSize, IndexedColors colorIndex) {
  270. cellStyle.setBorderBottom(borderSize);
  271. cellStyle.setBottomBorderColor(colorIndex.index);
  272. cellStyle.setBorderLeft(borderSize);
  273. cellStyle.setLeftBorderColor(colorIndex.index);
  274. cellStyle.setBorderRight(borderSize);
  275. cellStyle.setRightBorderColor(colorIndex.index);
  276. cellStyle.setBorderTop(borderSize);
  277. cellStyle.setTopBorderColor(colorIndex.index);
  278. }
  279.  
  280. private static void setColor(CellStyle cellStyle, short color, FillPatternType fillPattern) {
  281. cellStyle.setFillForegroundColor(color);
  282. cellStyle.setFillPattern(fillPattern);
  283. }
  284. }

java导出标题多行且合并单元格的EXCEL的更多相关文章

  1. Java Controller下兼容xls和xlsx且可识别合并单元格的excel导入功能

    1.工具类,读取单元格数据的时候,如果当前单元格是合并单元格,会自动读取合并单元格的值 package com.shjh.core.util; import java.io.IOException; ...

  2. 【记录】解析具有合并单元格的Excel

    最近公司让做各种数据表格的导入导出,就涉及到电子表格的解析,做了这么多天总结一下心得. 工具:NOPI 语言:C# 目的:因为涉及到导入到数据库,具有合并单元格的多行必然要拆分,而NPOI自动解析的时 ...

  3. 【开发者笔记】解析具有合并单元格的Excel

    最近公司让做各种数据表格的导入导出,就涉及到电子表格的解析,做了这么多天总结一下心得. 工具:NOPI 语言:C# 目的:因为涉及到导入到数据库,具有合并单元格的多行必然要拆分,而NPOI自动解析的时 ...

  4. python-利用xlrd模块中读取有合并单元格的excel数据

    前言 对于excel中有合并单元格的情况,合并的单元格只能取到第一个单元格的值,合并的单元格后面的单元格内容的值为空,针对这个情况,写了下面一段代码实现, 对单元格进行判断,如果是传入的索引是合并单元 ...

  5. (二)数据源处理3-python处理包含合并单元格的excel

    分析:

  6. 让我头疼一下午的Excel合并单元格

    Excel导出常见问题 excel导出其实不算什么难事 在网上copy下模板代码,填充自己的业务数据,提供一个http接口基本就可以得到你要导出的数据了. 但是,凡事都有例外,截止今天,excel导出 ...

  7. POI导入具有合并了单元格的Excel

    POI进行单行单行地导入的数据在网上有许多的文章,但是要导入一个具有合并单元格的excel貌似比较难找.刚好最近完成了这样的一个需求,要求导入具有合并单元格的excel: /** * 读取excel数 ...

  8. GridView中合并单元格

    using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Da ...

  9. 复杂的POI导出Excel表格(多行表头、合并单元格)

    poi导出excel有两种方式: 第一种:从无到有的创建整个excel,通过HSSFWorkbook,HSSFSheet HSSFCell, 等对象一步一步的创建出工作簿,sheet,和单元格,并添加 ...

随机推荐

  1. SSM整合教程

    接着一直next下去 创建各个目录 pom.xml文件中引入各种包 <?xml version="1.0" encoding="UTF-8"?> & ...

  2. Redis 设计与实现,看 SDS(Simple Dynamic String) 感悟

    Redis 设计与实现,看 SDS(Simple Dynamic String) 感悟 今天在看 Redis 设计与实现这本书的时候,发现了里面系统定义的数据结构 SDS,中文名为 简单动态字符串.对 ...

  3. 阿里开源 Dragonwell JDK 重磅发布 GA 版本:生产环境可用

    今年 3 月份,阿里巴巴重磅开源 OpenJDK 长期支持版本 Alibaba Dragonwell的消息,在很长一段时间内都是开发者的讨论焦点,该项目在 Github 上的 Star 数迅速突破 1 ...

  4. Python - 字典 - 第十天

    Python 字典 字典是另一种可变容器模型,且可存储任意类型对象. 字典的每个键值(key=>value)对用冒号(:)分割,每个对之间用逗号(,)分割,整个字典包括在花括号({})中 ,格式 ...

  5. Flask笔记:cookie

    在网站中,HTTP请求是无状态的:第一次请求成功后,第二次请求时服务器依然不知道这次请求的所属用户是谁.为了解决这个问题,在第一次请求成功后,服务器会生成并返回对应的cookie信息给浏览器,而浏览器 ...

  6. Codeforces 939A题,B题(水题)

    题目链接:http://codeforces.com/problemset/problem/939/A A题 A. Love Triangle time limit per test 1 second ...

  7. Linux命令行基本数据库语句

    -- 数据库的操作 -- 链接数据库 mysql -uroot -p mysql -uroot -pmysql -- 退出数据库 exit/quit/ctrl+d -- sql语句最后需要有分号;结尾 ...

  8. Ext中statics()与self

    var self = this; var statics = self.statics();//所在类的静态成员(instance.statics():跟着所在类走,在哪个类中,就返回哪个类中的静态成 ...

  9. Windows+Qt使用gRPC

    上篇文章<Windows+VS2017使用gRPC>编译出了Windows下可用的gRPC静态lib库文件,在此基础上要想在Qt上使用,需要使用MSVC2017 64bit构建组件进行构建 ...

  10. rhel安装输入法

    # yum install "@Chinese Support" 安装完成后,设置输入法: System -> Preferences -> Input Method