poi 动态生成多表头execl

Posted zhangdke

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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;
}


}

以上是关于poi 动态生成多表头execl的主要内容,如果未能解决你的问题,请参考以下文章

如何在ios中动态生成多页pdf时处理分页

java集成poi报表,导出xlsxlsx,表头,内容,sheet动态数据填充,局部单元格颜色字体设置

mPDF 中动态生成页面的页码

初学js---动态生成表格

EasyUi datagrid 动态生成表头

POI动态生成Excel