java
package ddd.special.infrastructure.utils;
import lombok.SneakyThrows;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.*;
import javax.servlet.http.HttpServletResponse;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.*;
/**
* 完整版导出excel,行和列,分级别表头
*/
public class EUtils {
//记录起始行数
static int a =0;
//记录列坐标
static int rank =0;
//用来计算 该单元格的高度n行
static int total=0;
//记录row行坐标,因为该api生成表格原则是逐行row生成
static Set<Integer> set=new HashSet<>();
//记录每个单元格个坐标
static List<Object> list =new ArrayList<>();
//记录合并单元格,集体合并,并生成样式,解决合并后样式问题
static List<CellRangeAddress> listRegion =new ArrayList<>();
public static void main(String[] args) throws IOException {
List<Map<String,Object>> menuList=new ArrayList<>();
Map<String,Object> map1=new HashMap<>();
map1.put("text","1单元");
Map<String,Object> map2=new HashMap<>();
map2.put("text","2单元");
Map<String,Object> map3=new HashMap<>();
map3.put("text","3单元");
List<Map<String,Object>> menuList2=new ArrayList<>();
menuList2.add(map1);
menuList2.add(map2);
menuList2.add(map3);
Map<String,Object> map=new HashMap<>();
map.put("children",menuList2);
map.put("text","李二");
menuList.add(map);
System.out.println(menuList);
Map<String,String> header = new LinkedHashMap<>();
header.put("a","标题1");
header.put("b","标题2");
header.put("c","标题3");
header.put("d","标题4");
header.put("e","标题5");
List<Map<String,Object>> body = new ArrayList<>();
Map<String,Object> map4=new HashMap<>();
map4.put("a","1");
map4.put("b","2");
map4.put("c","3");
map4.put("d","4");
map4.put("e","5");
body.add(map4);
Thread thread1 = new Thread(()->excelLocal(menuList,header,body,"qqq1"));
Thread thread2 = new Thread(()->excelLocal(menuList,header,body,"qqq2"));
thread1.start();
thread2.start();
try {
//两个线程必须都执行完毕后
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("两个线程都已经执行完毕");
}
//本地测试下载
@SneakyThrows
public static synchronized void excelLocal(List<Map<String,Object>> menuList,Map<String,String> header, List<Map<String,Object>> body,String fileName) {
System.out.println("线程名:" + Thread.currentThread().getName() + ",运行开始");
Thread.sleep(2000);
XSSFWorkbook wb = handlerContext(menuList,header,body,fileName);
OutputStream out= new FileOutputStream("E:\\"+fileName+".xlsx");
wb.write(out);
out.flush();
System.out.println("线程名:" + Thread.currentThread().getName() + ",运行结束");
}
//暴露对外端口,实现响应到客户端下载
public static void excelExport(List<Map<String,Object>> menuList,Map<String,String> header, List<Map<String,Object>> body,String fileName, HttpServletResponse response) throws IOException {
XSSFWorkbook wb = handlerContext(menuList,header,body,fileName);
response.setCharacterEncoding("UTF-8");
response.setContentType("application/vnd.ms-excel;charset=UTF-8");
response.addHeader("Content-Disposition", "attachment; filename=" + new String((fileName+".xlsx").getBytes("UTF-8"), "ISO8859-1"));
wb.write(response.getOutputStream());
}
public static XSSFWorkbook handlerContext(List<Map<String,Object>> menuList,Map<String,String> header, List<Map<String,Object>> body,String fileName){
//创建HSSFWorkbook对象----------表格文档对象
XSSFWorkbook wb = new XSSFWorkbook();
//创建HSSFSheet对象----------------创建表单对象
XSSFSheet sheet = wb.createSheet(fileName);
return count(menuList,setContent(wb,sheet,header,body,fileName),sheet);
}
private static XSSFWorkbook count(List<Map<String,Object>> menuList,XSSFWorkbook wb,XSSFSheet sheet) {
try {
XSSFRow row = null;
//起始行+2
a=2;
//进行树状数据运算,计算下标
for (Map<String,Object> menu: menuList) {
toData(menu);
rank=0;
}
//生成行标题单元格
Iterator<Integer> iterator = set.iterator();
while (iterator.hasNext()){
int with=iterator.next();
row = sheet.getRow(with)==null?sheet.createRow(with):sheet.getRow(with);
row.setHeight((short) (30 * 20));
for (Object l : list) {
List value = (ArrayList)l;
Object o = value.get(0);
Object o1 = value.get(1);
Object o2 = value.get(2);
if (with==(int)o){
XSSFCell cell = row.createCell((int)o1);
cell.setCellValue((String) o2);
cell.setCellStyle(style2(wb));
}
}
}
//合并单元格
for (CellRangeAddress region : listRegion ) {
sheet.addMergedRegion(region);
}
return wb;
}catch (Throwable e){
e.printStackTrace();
return null;
}finally {
//记录起始行数
a =0;
//记录列坐标
rank =0;
//用来计算 该单元格的高度n行
total=0;
//记录row行坐标,因为该api生成表格原则是逐行row生成
set=new HashSet<>();
//记录每个单元格个坐标
list =new ArrayList<>();
//记录合并单元格,集体合并,并生成样式,解决合并后样式问题
listRegion =new ArrayList<>();
}
}
//计算每个单元格坐标
private static void toData(Map<String,Object> m) {
total=0;
toTotal(m);
int i=total==0?1:total;
int with=i-1+a;
if(a!=with) {
CellRangeAddress region = new CellRangeAddress(a, with, rank, rank);
listRegion.add(region);
}
List<Object> list1 =new ArrayList<>();
list1.add(a);
list1.add(rank);
list1.add(m.get("text"));
list1.add(with);
list.add(list1);
set.add(a);
rank++;
List<Map<String,Object>> children = (List<Map<String, Object>>) m.get("children");
if (children==null||children.size()==0){
a=a+total;
}
if (children!=null&&children.size()!=0){
for (Map<String,Object> menu: children ) {
toData(menu);
rank--;
}
}
}
//计算当前单元格高度为n行
private static void toTotal(Map<String,Object> m) {
List<Map<String,Object>> children = (List<Map<String,Object>>)m.get("children");
if (children==null||children.size()==0){
total++;
return;
}
children.stream().forEach(EUtils::toTotal);
}
//样式1标题行格式
private static XSSFCellStyle style1(XSSFWorkbook wb){
// 设置这些样式
XSSFCellStyle style = wb.createCellStyle();
// style.setBorderBottom(HSSFCellStyle.BORDER_THIN); //下边框
// style.setBorderLeft(HSSFCellStyle.BORDER_THIN);//左边框
// style.setBorderTop(HSSFCellStyle.BORDER_THIN);//上边框
// style.setBorderRight(HSSFCellStyle.BORDER_THIN);//右边框
// style.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);//垂直bai
// style.setAlignment(HSSFCellStyle.ALIGN_CENTER);//水平du
style.setWrapText(true);//设置自动换行
XSSFFont font = wb.createFont();
font.setBold(true);
font.setFontName("宋体");
font.setFontHeightInPoints((short) 13);
style.setFont(font);
return style;
}
//样式2身体格式
private static XSSFCellStyle style2(XSSFWorkbook wb){
XSSFCellStyle style = wb.createCellStyle();
// style.setBorderBottom(HSSFCellStyle.BORDER_THIN); //下边框
// style.setBorderLeft(HSSFCellStyle.BORDER_THIN);//左边框
// style.setBorderTop(HSSFCellStyle.BORDER_THIN);//上边框
// style.setBorderRight(HSSFCellStyle.BORDER_THIN);//右边框
style.setVerticalAlignment(VerticalAlignment.CENTER);//垂直bai
// style.setAlignment(HSSFCellStyle.ALIGN_CENTER);//水平du
// style.setWrapText(true);//设置自动换行
// 生成一个字体
XSSFFont font = wb.createFont();
// font.setBoldweight(XSSFFont.BOLDWEIGHT_BOLD);//加粗
font.setFontName("宋体");
font.setFontHeightInPoints((short) 11);
// 把字体应用到当前的样式
style.setFont(font);
return style;
}
private static XSSFWorkbook setContent( XSSFWorkbook wb, XSSFSheet sheet ,Map<String,String> header, List<Map<String,Object>> body, String fileName) {
//创建HSSFSheet对象----------------创建表单对象
sheet.setDefaultColumnWidth(20);
// 产生表格抬头行
XSSFCell cellHeader;
XSSFRow row = sheet.createRow(0);
cellHeader = row.createCell(0);
row.setHeight((short) (30 * 12));
cellHeader.setCellStyle(style1(wb));
cellHeader.setCellValue(fileName);
//合并
sheet.addMergedRegion(new CellRangeAddress(0,0,0,header.size()-1));
//生成列标题
row = sheet.createRow(1);
int j =0;
for (String key :header.keySet()) {
cellHeader = row.createCell(j);
row.setHeight((short) (30 * 20));
cellHeader.setCellStyle(style1(wb));
cellHeader.setCellValue(header.get(key));
//自适应行宽
sheet.autoSizeColumn(j);
sheet.setColumnWidth(j, sheet.getColumnWidth(j) * 17 / 10);
j++;
}
//生成身体数据
for (int x = 0; x < body.size(); x++) {
Map<String, Object> data = body.get(x);
row = sheet.createRow(x+2);
for (int i=0;i<header.keySet().size();i++) {
cellHeader = row.createCell(i);
row.setHeight((short) (30 * 20));
cellHeader.setCellValue((String) data.get(header.get(i)));
}
int y = 0;
for (String key :header.keySet()) {
cellHeader = row.createCell(y);
row.setHeight((short) (30 * 20));
cellHeader.setCellValue((String) data.get(key));
y++;
}
}
return wb;
}
}
运行控制台:
运行结果