poi 动态生成多表头execl

如果所示,我要导出的数据界面是下面这样的,前面样品编号、样品名称、炉次为主表数据,而检验结果是子表数据,这里子表的数据作为了主表的数据的一个字段(集合属性),下面代码会给大家看vo结构

下图为要导出的execl效果

开发思路:

1、该表头表体是根据主表样品名称不同而子表元素个数就会不同,所以第一步就是将前端传来的数据按样品名称分组

2、由于导出的数据顺序会乱,所以还是需要map排序下,这里我们可以按key排序

3、由于我要导出的数据是主子表结构,所以要将表体数据利用反射映射到表头去,以方便导出使用,这里有个细节就是最终组成的map其实现类要用LinkedHashMap这样才能保证顺序不乱

主表结构如下:

子表结构如下:

组装数据如下:

4、组装导出信息

5、组好数据去调用写的导出工具类解析表头表体写进相应行设置样式,然后写出流就可以了

下面贴出主要代码,两个实体类这里就不贴了

pom文件先导入依赖

<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.14</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.14</version>
</dependency>

@RequestMapping(value="/excelExport",method=RequestMethod.POST)
public @ResponseBody Result exportData(@RequestParam String exportdata, HttpServletRequest request, HttpServletResponse response){
Result result=new Result();
try {
List<TestResultVO> list = JSONArray.parseArray(exportdata, TestResultVO.class);
Map<String, List<TestResultVO>> groupmap = list.stream().collect(Collectors.groupingBy(TestResultVO :: getSamplename));//按样品名称分组(元素相同的一组制作表头)
Map<String, List<TestResultVO>> sortmap = this.sortByKey(groupmap, false);//升序排序
Map<String, List<TestResultVO>> exportMap = new LinkedHashMap<String, List<TestResultVO>>();
for (Map.Entry<String, List<TestResultVO>> entry : sortmap.entrySet()) {
//制作表头
StringBuffer sb = new StringBuffer("{‘sampleno‘:‘样品编号‘,‘samplename‘:‘样品名称‘");
List<TestResultVO> testvos = entry.getValue();
for (TestResultVO vo : testvos) {
List<ResultVO> resultVOs = vo.getResults();
if (resultVOs!=null && resultVOs.size()>0) {
Field fields[]=vo.getClass().getDeclaredFields();
for (int i = 0; i < resultVOs.size(); i++) {
if (i==resultVOs.size()-1) {
sb.append(",‘element").append(i+1).append("‘:‘").append(resultVOs.get(i).getItemname()).append("‘}");
}else {
sb.append(",‘element").append(i+1).append("‘:‘").append(resultVOs.get(i).getItemname()).append("‘");
}
//将子表元素值反射到主表自定义字段,以便导出使用
for (Field field : fields) {
String lastzf = null;
if (i>8) {//将子表元素根据下标+1与主表截取最后两位相同的赋值
lastzf = field.getName().substring(field.getName().length()-2);
}else {//将子表元素根据下标+1与主表截取最后一位相同的赋值
lastzf = field.getName().substring(field.getName().length()-1);
}
if (NumberUtils.isDigits(lastzf)) {//判断该类型是否为整数字符串
if(Integer.valueOf(lastzf)==i+1){
field.setAccessible(true);
field.set(vo, resultVOs.get(i).getConfirmvalue());
field.setAccessible(false); 
}
}
}
}

}else {
sb.append("}");
}
}

Boolean falg = true;//这段逻辑处理不同元素名称,相同元素个数的情况
for (Map.Entry<String, List<TestResultVO>> enMap : exportMap.entrySet()) {
if (enMap.getKey().contains(sb.toString())) {
falg = false;
enMap.getValue().addAll(entry.getValue());
}
}
if (falg) {
exportMap.put(sb.toString(), entry.getValue());
}

}
// 获取浏览器信息,对文件名进行重新编码
String fileName = "化验结果查询.xlsx";
String agent = request.getHeader("User-Agent"); // 获取浏览器
if (agent.contains("Firefox")) {
Base64Encoder base64Encoder = new Base64Encoder();
fileName = "=?utf-8?B?" + base64Encoder.encode(fileName.getBytes("utf-8")) + "?=";
} else if (agent.contains("MSIE")) {
fileName = URLEncoder.encode(fileName, "utf-8");
} else if (agent.contains("Safari")) {
fileName = new String(fileName.getBytes("utf-8"), "ISO8859-1");
} else {
fileName = URLEncoder.encode(fileName, "utf-8");
}
// 设置返回的信息头
response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
response.setContentType("application/vnd.ms-excel");

// Excel写入
OutputStream os = response.getOutputStream();

ExcelExportImportUtils.ListtoExecl(exportMap, os);

// 关闭资源
os.flush();
os.close();
} catch (Exception e) {
result = ExceptionResult.process(e);
}
log.info("end method exportData,return"+result);
return result;
}

/**
* @Title: sortByKey 
* @author zhangdke
* @Description: map 按key排序
* @param map 要排序的map
* @param isDesc 是否降序,true降序,false升序
* @return Map<K,V> 
* @throws
*/
public static <K extends Comparable<? super K>, V> Map<K, V> sortByKey(Map<K, V> map, boolean isDesc) {
Map<K, V> result = Maps.newLinkedHashMap();
if (isDesc) {
map.entrySet().stream().sorted(Map.Entry.<K, V>comparingByKey().reversed())
.forEachOrdered(e -> result.put(e.getKey(), e.getValue()));
} else {
map.entrySet().stream().sorted(Map.Entry.<K, V>comparingByKey())
.forEachOrdered(e -> result.put(e.getKey(), e.getValue()));
}
return result;
}

package com.yonyou.scm.qc.core.pub;

import java.io.OutputStream;
import java.lang.reflect.Field;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import net.sf.json.JSONObject;

import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFFont;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

/**
* Excel导出数据工具类

* @author zhangdke
* @date 2019年12月4日
*
*/
public class ExcelExportImportUtils {

/**
* @Title: 生成动态多表头execl
* @author zhangdke
* @Description: TODO
* @param map
* @param out
* @return void 
* @throws
*/
public static <T> void ListtoExecl(Map<String, List<T>> map,
OutputStream out) throws Exception {
XSSFWorkbook workbook = new XSSFWorkbook();
// 如果导入数据为空,则抛出异常。
if (map.isEmpty()) {
workbook.close();
throw new Exception("导入的数据为空");
}
XSSFCellStyle headStyle = getHeadStyle(workbook);//表头样式
XSSFCellStyle bodyStyle = getBodyStyle(workbook);//表体样式
int size = 0;//行
XSSFSheet sheet = workbook.createSheet();
for (Map.Entry<String, List<T>> entry : map.entrySet()) {
List<T> list = entry.getValue();
Map<String, String> fields = (Map<String, String>) JSONObject
.fromObject(entry.getKey());
// 提取表格的字段名(英文字段名是为了对照中文字段名的)
String[] egtitles = new String[fields.size()];
String[] cntitles = new String[fields.size()];
Iterator<String> it = fields.keySet().iterator();
int count = 0;
while (it.hasNext()) {
String egtitle = (String) it.next();
String cntitle = fields.get(egtitle);
egtitles[count] = egtitle;
cntitles[count] = cntitle;
count++;
}
XSSFRow row = sheet.createRow(size);
// 添加表头信息
for (int f = 0; f < cntitles.length; f++) {
XSSFCell cell = row.createCell(f);
cell.setCellValue(cntitles[f]);
cell.setCellStyle(headStyle);
}
for (T t : list) {
size++;
row = sheet.createRow(size);
for (int h = 0; h < cntitles.length; h++) {
Field fd = t.getClass().getDeclaredField(egtitles[h]);
fd.setAccessible(true);
Object o = fd.get(t);
String value = o == null ? "" : o.toString();
XSSFCell cell = row.createCell(h);
cell.setCellValue(value);
cell.setCellStyle(bodyStyle);
}

}
size++;

// 必须在单元格设值以后进行
// 设置为根据内容自动调整列宽
for (int k = 0; k < cntitles.length; k++) {
sheet.autoSizeColumn(k);
}

// 处理中文不能自动调整列宽的问题
setSizeColumn(sheet, cntitles.length);

}
// 将创建好的数据写入输出流
workbook.write(out);
// 关闭workbook
workbook.close();
}

/**
* @Title: 自适应列宽度中文支持 
* @author zhangdke
* @Description: TODO
* @param sheet
* @param size 
* @return void 
* @throws
*/
private static void setSizeColumn(XSSFSheet sheet, int size) {
for (int columnNum = 0; columnNum < size; columnNum++) {
int columnWidth = sheet.getColumnWidth(columnNum) / 256;
for (int rowNum = 0; rowNum < sheet.getLastRowNum(); rowNum++) {
XSSFRow currentRow;
// 当前行未被使用过
if (sheet.getRow(rowNum) == null) {
currentRow = sheet.createRow(rowNum);
} else {
currentRow = sheet.getRow(rowNum);
}

if (currentRow.getCell(columnNum) != null) {
XSSFCell currentCell = currentRow.getCell(columnNum);
if (currentCell.getCellType() == XSSFCell.CELL_TYPE_STRING) {
int length = currentCell.getStringCellValue()
.getBytes().length;
if (columnWidth < length) {
columnWidth = length;
}
}
}
}
sheet.setColumnWidth(columnNum, columnWidth * 256);
}
}

/**
* @Title: 表头样式 
* @author zhangdke
* @Description: TODO
* @param workbook
* @return XSSFCellStyle 
* @throws
*/
private static XSSFCellStyle getHeadStyle(XSSFWorkbook workbook){
// 字体样式
XSSFFont xssfFont = workbook.createFont();
// 加粗
xssfFont.setBold(true);
// 字体名称
xssfFont.setFontName("楷体");
// 字体大小
xssfFont.setFontHeight(12);
// 表头样式
XSSFCellStyle headStyle = workbook.createCellStyle();
// 设置字体css
headStyle.setFont(xssfFont);
// 竖向居中
headStyle.setVerticalAlignment(VerticalAlignment.CENTER);
// 横向居中
headStyle.setAlignment(HorizontalAlignment.CENTER);
// 边框
headStyle.setBorderBottom(BorderStyle.THIN);
headStyle.setBorderLeft(BorderStyle.THIN);
headStyle.setBorderRight(BorderStyle.THIN);
headStyle.setBorderTop(BorderStyle.THIN);
return headStyle;
}

/**
* @Title: 表体样式 
* @author zhangdke
* @Description: TODO
* @param workbook
* @return XSSFCellStyle 
* @throws
*/
private static XSSFCellStyle getBodyStyle(XSSFWorkbook workbook){
// 内容字体样式
XSSFFont contFont = workbook.createFont();
// 加粗
contFont.setBold(false);
// 字体名称
contFont.setFontName("楷体");
// 字体大小
contFont.setFontHeight(11);
// 内容样式
XSSFCellStyle bodyStyle = workbook.createCellStyle();
// 设置字体css
bodyStyle.setFont(contFont);
// 竖向居中
bodyStyle.setVerticalAlignment(VerticalAlignment.CENTER);
// 横向居中
bodyStyle.setAlignment(HorizontalAlignment.CENTER);
// 边框
bodyStyle.setBorderBottom(BorderStyle.THIN);
bodyStyle.setBorderLeft(BorderStyle.THIN);
bodyStyle.setBorderRight(BorderStyle.THIN);
bodyStyle.setBorderTop(BorderStyle.THIN);
return bodyStyle;
}

}

原文地址:https://www.cnblogs.com/zhangdke/p/12425041.html

时间: 2024-11-12 22:51:28

poi 动态生成多表头execl的相关文章

JAVA操作Excel 可配置,动态 生成复杂表头 复杂的中国式报表表头

转载:开源社区http://www.oschina.net/code/snippet_1424099_49530?p=2代码] [Java]代码 该代码实现了Excel复杂表头的生成 基于sql server 的一张树表 你们可以 看代码自行建库 package com.jingjiu.util; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; i

POI动态生成word2007加强版

先看效果图: public class GeneralTemplateWord2007Util { public static void main(String[] args) { // TODO Auto-generated method stub String filePath = "C:/Users/Administrator/Desktop/doc/模板.docx"; String outFile = "C:/Users/Administrator/Desktop/生

element UI实现动态生成多级表头

一.效果图 二.封装两个组件,分别为DynamicTable.vue和TableColumn.vue,TableColumn.vue主要是使用递归来对表头进行循环生成 DynamicTable.vue 1 <template> 2 <el-table :data="tableData" border :height="height"> 3 <template v-for="item in tableHeader"&g

使用aspose.cell动态导出多表头 EXCEL

效果图: 前台调用: using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using System.Data; using ExportCells; namespace WebApplication1 { public partial class _Default : Sy

Silverlight系列--动态生成DataGrid列 根据动态列绑定Dictionary数据

1.获取表头数据源动态生成DataGrid表头 DataGridTextColumn d = new DataGridTextColumn(); d.Header = itemPriceClass.PriceKindCode + itemPriceClass.PriceKindName; Binding bin = new Binding(); bin.Converter = new RowIndexConverter(); bin.ConverterParameter = itemPriceC

C++实现根据类名动态生成类对象

在开发后台服务的过程中,我们常常需要从数据库中取数据,并将数据缓存在本地中,另外,我们的服务还需要有更新数据的能力:包括定时的主动更新以及数据库数据更新时服务收到通知的被动更新. 之前在需要用到以上功能的时候,模仿着组内通用的数据Cache部分的代码来写,十分方便,基本上只需要自己写两个类:一个是取数据并缓存数据的类XXXData,一个是扇出数据的类XXXFetcher. 在需要使用数据的时候,通过: FetcherFactory::getFetcher<XXXFetcher>() 即可获取一

用Aspose.Words for .NET动态生成word文档中的数据表格

1.概述 最近项目中有一个这样的需求:导出word 文档,要求这个文档的格式不是固定的,用户可以随便的调整,导出内容中的数据表格列是动态的,例如要求导出姓名和性别,你就要导出这两列的数据,而且这个文档不是导出来之后再调整而是导出来后已经是调整过了的.看到这里,您也许马上想到用模板导出!而且.NET中自带有这个组件:Microsoft.Office.Interop.Word,暂且可以满足需求吧.但这个组件也是有局限性的,例如客户端必须装 office组件,而且编码复杂度高.最麻烦的需求是后面那个-

项目总结—jQuery EasyUI-DataGrid动态加载表头

http://blog.csdn.net/zwk626542417/article/details/19248747 概要 在前面两篇文章中,我们已经介绍了在jQuery EasyUI-DataGrid中有参数和无参数的情况下将数据库中数据显示到前台,但是对于前面两篇文章显示的数据表头是固定的,如果我们显示到前台的数据来自数据库不同的表,那么表头也需要动态的加载,这篇文章我们就来看下在EasyUI-DataGrid中动态加载表头和数据. 实现 我们要实现的功能是根据我们的需要,让DataGrid

动态生成table

需要局部刷新table的数据,就需要使用到js控制生成table: html:table的表头是固定不变的. <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <title>动态生成列表</title> <script src="http://code.jquery.com/jquery-1.9.1