导出工具类

package com.szjz.service.edu.utils;

import cn.hutool.core.util.StrUtil;
import org.apache.poi.hssf.usermodel.HSSFPalette;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;

import javax.exception.BizException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.*;

/**
* 报表公共导出工具
*
* @author lyj
* @description
* @date 2020年6月22日
*/
public class ExcelExportUtil {

/**
* 导出工具:返回文件地址方式
* head:最上部标题
* foot:底部备注
* titile:表头
* par: 表头对应参数名
* list:数据
* fileName:导出文件名
* dirPath:本地路径
* combineFlag:是否开启合并功能(只支持合并行),如果开启,传入值必须存在combine列(合并行数)
* combineCells:需要合并的列
* formatList:格式化数据
*/
public static String export(List<Map<String, Object>> head, String foot, List<Map<String, Object>> titleList, int totalCol, List<Map<String, Object>> dataList, String[] title, String[] par, List<Map<String, Object>> list, String fileName,
String dirPath, Boolean combineFlag, List<String> combineCells, Map<String, Object> formatList) throws Exception {
String localFilePath = "";
OutputStream outputStream = null;
try {
HSSFWorkbook wb = exportBase(head, foot, titleList, totalCol,dataList, title, par, list, combineFlag, combineCells, formatList);

// String localFileName = "C:/cloud";
String localFileName = "C:";
localFilePath = localFileName + "/" + dirPath + "/" + fileName;
File dir = new File(localFileName);
// 如果文件夹不存在则创建
if (!dir.exists() && !dir.isDirectory()) {
dir.mkdir();
}
File dirs = new File(localFileName + "/" + dirPath + "/");
// 如果文件夹不存在则创建
if (!dirs.exists() && !dirs.isDirectory()) {
dirs.mkdir();
}
outputStream = new FileOutputStream(localFilePath);
wb.write(outputStream);

} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (outputStream != null) {
outputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}

return localFilePath;
}

/**
* 简化版
* 导出工具:返回IO流
* head:最上部标题 CONTENT:内容
* foot:底部备注
* totalCol: 总列数
* titile:最小表头
* par: 表头对应参数名
* list:数据
* fileName:导出文件名
* formatList:格式化数据
*/
public static void exportIo(List<Map<String, Object>> head, String foot, int totalCol,
String[] title, String[] par, List<Map<String, Object>> list, String fileName,
Map<String, Object> formatList, HttpServletRequest request, HttpServletResponse response) throws Exception {
OutputStream outputStream = null;
try {
HSSFWorkbook wb = exportBase(head, foot, null, totalCol, null, title, par, list, null, null, formatList);

// 第七步,将文件存到流中
response.setCharacterEncoding("UTF-8");
response.setContentType("multipart/form-data");
/*
* 解决各浏览器的中文乱码问题
*/
String userAgent = request.getHeader("User-Agent");
byte[] bytes = userAgent.contains("MSIE") ? fileName.getBytes() : fileName.getBytes("UTF-8"); // fileName.getBytes("UTF-8")处理safari的乱码问题
fileName = new String(bytes, "ISO-8859-1"); // 各浏览器基本都支持ISO编码
response.setHeader("Content-disposition", "attachment;filename=" + fileName);
wb.write(response.getOutputStream());
} catch (Exception e) {
e.printStackTrace();
throw new Exception("导出失败");
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

/**
* 导出工具:返回IO流
* head:最上部标题
* foot:底部备注
* titleList: 表头合并明细(如果有则根据明细处理表头)
* totalCol: 总列数
* titile:最小表头
* par: 表头对应参数名
* list:数据
* fileName:导出文件名
* dirPath:本地路径
* combineFlag:是否开启合并功能(只支持合并行),如果开启,传入值必须存在combine列(合并行数)
* combineCells:需要合并的列
* formatList:格式化数据
*/
public static void exportIo(List<Map<String, Object>> head, String foot, List<Map<String, Object>> titleList, int totalCol, List<Map<String, Object>> dataList,
String[] title, String[] par, List<Map<String, Object>> list, String fileName,
Boolean combineFlag, List<String> combineCells, Map<String, Object> formatList, HttpServletRequest request, HttpServletResponse response) throws Exception {
OutputStream outputStream = null;
try {
HSSFWorkbook wb = exportBase(head, foot, titleList, totalCol, dataList, title, par, list, combineFlag, combineCells, formatList);

// 第七步,将文件存到流中
response.setCharacterEncoding("UTF-8");
response.setContentType("multipart/form-data");
/*
* 解决各浏览器的中文乱码问题
*/
String userAgent = request.getHeader("User-Agent");
byte[] bytes = userAgent.contains("MSIE") ? fileName.getBytes() : fileName.getBytes("UTF-8"); // fileName.getBytes("UTF-8")处理safari的乱码问题
fileName = new String(bytes, "ISO-8859-1"); // 各浏览器基本都支持ISO编码
response.setHeader("Content-disposition", "attachment;filename=" + fileName);
wb.write(response.getOutputStream());
} catch (Exception e) {
e.printStackTrace();
throw new Exception("导出失败");
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

//导出核心方法
private static HSSFWorkbook exportBase(List<Map<String, Object>> headList, String foot, List<Map<String, Object>> titleList, int totalCol, List<Map<String, Object>> dataList, String[] title, String[] par,
List<Map<String, Object>> list, Boolean combineFlag,
List<String> combineCells, Map<String, Object> formatList) throws Exception {
if (titleList == null&&title!=null && title.length != par.length) {
throw new Exception("表头数和参数长度必须一致");
}
HSSFWorkbook wb = new HSSFWorkbook();
// 第二步,在webbook中添加一个sheet,对应Excel文件中的 sheet
Sheet sheet = wb.createSheet("导出信息");
// 第三步,在sheet中添加表头第0行,注意老版本poi对Excel的行数列数有限制
// 设置默认列宽
sheet.setDefaultColumnWidth(8);
Row row = null;
// 第四步,创建单元格样式:
Font font = wb.createFont();
font.setFontName("宋体");
// 标题样式
// 设置自定义颜色
HSSFPalette customPalette = wb.getCustomPalette();
customPalette.setColorAtIndex((short) 1, (byte) 128, (byte) 198, (byte) 135);
CellStyle titleStyle = wb.createCellStyle();
titleStyle.setAlignment(HorizontalAlignment.CENTER);
titleStyle.setVerticalAlignment(VerticalAlignment.CENTER);
titleStyle.setFont(font);
// titleStyle.setFillForegroundColor(HSSFColor.LIGHT_GREEN.index);//
// 设置前景色128,198,135
// titleStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
titleStyle.setBorderBottom(BorderStyle.valueOf((short) 1));
titleStyle.setBorderLeft(BorderStyle.valueOf((short) 1));
titleStyle.setBorderRight(BorderStyle.valueOf((short) 1));
titleStyle.setBorderTop(BorderStyle.valueOf((short) 1));
// 居中样式
CellStyle styleCenter = wb.createCellStyle();
styleCenter.setAlignment(HorizontalAlignment.CENTER);
styleCenter.setVerticalAlignment(VerticalAlignment.CENTER);
styleCenter.setBorderBottom(BorderStyle.valueOf((short) 1));
styleCenter.setBorderLeft(BorderStyle.valueOf((short) 1));
styleCenter.setBorderRight(BorderStyle.valueOf((short) 1));
styleCenter.setBorderTop(BorderStyle.valueOf((short) 1));
styleCenter.setWrapText(true);
//居右样式
CellStyle styleRight = wb.createCellStyle();
styleRight.setAlignment(HorizontalAlignment.RIGHT);
styleRight.setVerticalAlignment(VerticalAlignment.CENTER);
styleRight.setBorderBottom(BorderStyle.valueOf((short) 1));
styleRight.setBorderLeft(BorderStyle.valueOf((short) 1));
styleRight.setBorderRight(BorderStyle.valueOf((short) 1));
styleRight.setBorderTop(BorderStyle.valueOf((short) 1));
styleRight.setWrapText(true);
//居左样式
CellStyle styleLeft = wb.createCellStyle();
styleLeft.setAlignment(HorizontalAlignment.LEFT);
styleLeft.setVerticalAlignment(VerticalAlignment.CENTER);
styleLeft.setBorderBottom(BorderStyle.valueOf((short) 1));
styleLeft.setBorderLeft(BorderStyle.valueOf((short) 1));
styleLeft.setBorderRight(BorderStyle.valueOf((short) 1));
styleLeft.setBorderTop(BorderStyle.valueOf((short) 1));
styleLeft.setWrapText(true);
// 底部样式
CellStyle footStyle = wb.createCellStyle();
footStyle.setAlignment(HorizontalAlignment.LEFT);
footStyle.setVerticalAlignment(VerticalAlignment.TOP);
footStyle.setWrapText(true);
//表头样式
CellStyle headStyle = wb.createCellStyle();
Font font2 = wb.createFont();
font2.setFontName("宋体");
font2.setFontHeightInPoints((short) 16);
font2.setBold(true);
headStyle.setAlignment(HorizontalAlignment.CENTER);
headStyle.setVerticalAlignment(VerticalAlignment.CENTER);
headStyle.setBorderBottom(BorderStyle.THIN);
headStyle.setBorderLeft(BorderStyle.THIN);
headStyle.setBorderRight(BorderStyle.THIN);
headStyle.setBorderTop(BorderStyle.THIN);
headStyle.setFont(font2);

// 第五步,创建表头单元格,并设置样式
Cell cell;
//如果有顶部标题
int n = 0;
if (headList != null && headList.size() > 0) {
//表头占几行
n = headList.size();
for (int i = 0; i < headList.size(); i++) {
//创建行
row = sheet.createRow(i);
row.setHeightInPoints(35);// 设置行高
cell = row.createCell(0);
Map<String, Object> map = headList.get(i);
//内容
String content = map.get("CONTENT") != null ? map.get("CONTENT").toString() : "";
//样式 字体font 居左样式
if (map.size() > 1) {
setCellStyle(cell, sheet, row, wb, map, par.length);
} else {
//设置样式
cell.setCellStyle(headStyle);
//默认 合并列
CellRangeAddress region = new CellRangeAddress(0, 0, 0, totalCol - 1);
sheet.addMergedRegion(region);
}
//内容赋值
cell.setCellValue(content);

}
}
//特殊表头处理
if (titleList != null && titleList.size() > 0) {
int k=n;
for (int i = 0; i < titleList.size(); i++) {
Map<String, Object> headCombineMap = titleList.get(i);
//列明细
Object detail = headCombineMap.get("DETAIL");
if (detail instanceof Map) {
LinkedHashMap<String, Object> detailMap=(LinkedHashMap<String, Object>) detail;
if(detailMap.size()==0){
continue;
}
row = sheet.createRow(k + i);
row.setHeightInPoints(35);// 设置行高
//处理合并后边框问题
handleBorder(row, titleStyle, totalCol);
setHeadColumn(sheet, row, titleStyle, (LinkedHashMap<String, Object>) detail, totalCol);
} else {
continue;
}
//数据行
n++;
}
} else {
if(title!=null){
//使用简单title做表头
row = sheet.createRow(n);// 设置日期标题
row.setHeightInPoints(35);// 设置行高
for (int i = 0; i < title.length; i++) {
cell = row.createCell(i);
cell.setCellValue(title[i]);
cell.setCellStyle(titleStyle);
sheet.setColumnWidth(i, 6000);
}
n++;
}
}


//-------------合并用参数START-------------//
//合并开始行
Integer startIndex = 1;
//合并结束行
Integer endIndex = 1;
//需要增加数量
Integer currentIndex = 1;
Map<String,Object> combineTemp=new HashMap<>();
//如果带标题则开始行结束行都要加上表头数量
if (headList != null && headList.size() > 0) {
currentIndex = headList.size();
startIndex = headList.size();
}
//表头行数
if (titleList != null && titleList.size() > 0) {
currentIndex = titleList.size();
startIndex = titleList.size();
}
// 非配置导出包含标题和表头
if(headList != null && headList.size() > 0 && title != null && title.length > 0){
currentIndex = headList.size() + 1;
startIndex = headList.size() + 1;
}
//标题和表头都存在时
if (headList != null && headList.size() > 0&titleList != null && titleList.size() > 0) {
currentIndex = titleList.size()+headList.size();
startIndex = titleList.size()+headList.size();
}
//-------------合并用参数END-------------//
//是否存在数据特殊处理 目前只处理列合并
Map<String,String> dataMap=new HashMap<>();
if (dataList != null && dataList.size() > 0) {
for (Map<String, Object> map : dataList) {
if (map.get("COMBINE_DATAS") != null && StrUtil.isNotEmpty(map.get("COMBINE_DATAS").toString())) {
dataMap.put(map.get("CONTENT") + "", map.get("COMBINE_DATAS").toString());
}
}
}


for (int i = 0; i < list.size(); i++) {
row = sheet.createRow(i + n);
handleBorder(row, styleCenter, totalCol);
row.setHeightInPoints(25);// 设置行高
//数据填充
int skipNum=0;//需要跳过的数量
for (int j = 0; j < totalCol; j++) {
if(skipNum>0){
skipNum--;
continue;
}
cell = row.createCell(j);
//列参数名
String key = par[j];
String val = list.get(i).get(key) + "";
//查看字段是否需要特殊处理
String colCombine = list.get(i).get("COL_COMBINE") + "";
if("YES".equals(colCombine)&&dataMap.containsKey(key)){
String colCom=dataMap.get(key);
String startCol=colCom.split(",")[2];
String endCol=colCom.split(",")[3];
CellRangeAddress region = new CellRangeAddress(i + n, i + n, Integer.parseInt(startCol), Integer.parseInt(endCol));
sheet.addMergedRegion(region);
//计算需要跳过后面几次取值
skipNum=Integer.parseInt(endCol)-Integer.parseInt(startCol);
}
if (val == null || (val.length()) == 0 || "null".equals(val)) {
val = "";
}
cell.setCellValue(val);
int width=24;
//numChars*7+5]/7*256)/256
if(StrUtil.isNotEmpty(val)){
width=((val.length()*7+5)/7*256)/256;
if(width<24||val.length()>200){
width=30;
}
}
sheet.setColumnWidth(j, width*256);
//处理单元格样式
String alignment = checkAlignment(formatList, key);
if ("right".equals(alignment)) {
cell.setCellStyle(styleRight);
} else if ("left".equals(alignment)) {
cell.setCellStyle(styleLeft);
} else {
cell.setCellStyle(styleCenter);
}
}

if (combineFlag) {
//带合并导出
if (!list.get(i).containsKey("combine")) {
throw new Exception("使用带合并功能导出必须有combine(合并行数)列");
}
//合并行数
String combine = list.get(i).get("combine") + "";
//到达下次合并行数---公共
if ((i + currentIndex) == startIndex) {
endIndex = startIndex + Integer.parseInt(combine) - 1;
for (String str : combineCells) {
//跳过特殊字段
if (str.contains("_COMBINE")) {
continue;
}
//获取参数列索引
Integer cellIndex = 0;
for (int p = 0; p < par.length; p++) {
if (str.equals(par[p])) {
cellIndex = p;
break;
}
}
//合并数不为1时执行合并
if (!"1".equals(combine)) {
CellRangeAddress region = new CellRangeAddress(startIndex, endIndex, cellIndex, cellIndex);
sheet.addMergedRegion(region);
}
}
startIndex = endIndex + 1;
}
//处理特殊合并---指定行合并
for (String str : combineCells) {
if (!str.contains("_COMBINE")) {
continue;
}
//开始行,结束行
String colCombine = list.get(i).get(str) + "";
if("null".equals(colCombine)){
continue;
}
if (StrUtil.isEmpty(colCombine)) {
throw new BizException(str + "未设置值:开始行,结束行");
}
int colStartIndex = Integer.parseInt(colCombine.split(",")[0]);
int colEndIndex = Integer.parseInt(colCombine.split(",")[1]);
//获取参数列索引
Integer cellIndex = 0;
for (int p = 0; p < par.length; p++) {
if (str.equals(par[p]+"_COMBINE")) {
cellIndex = p;
break;
}
}
if (combineTemp.containsKey(colStartIndex + "," + colEndIndex + "," + cellIndex + "," + cellIndex)) {
continue;
}
combineTemp.put(colStartIndex + "," + colEndIndex + "," + cellIndex + "," + cellIndex,true);
//执行合并
CellRangeAddress region = new CellRangeAddress(colStartIndex, colEndIndex, cellIndex, cellIndex);
sheet.addMergedRegion(region);
}
}
}
//底部备注
if (StrUtil.isNotEmpty(foot)) {
row = sheet.createRow(list.size() + n + 3);
row.setHeightInPoints(25);// 设置行高
cell = row.createCell(0);
cell.setCellValue(foot);
cell.setCellStyle(footStyle);
//合并2行4列
CellRangeAddress region = new CellRangeAddress(list.size() + n + 3, list.size() + n + 5, 0, 3);
sheet.addMergedRegion(region);
}
return wb;
}

//设置表头数据
private static void setHeadColumn(Sheet sheet, Row row, CellStyle style, LinkedHashMap<String, Object> detail, int totalCol) {
//表头集合
List<String> titleList = new ArrayList<>();
for (String key : detail.keySet()) {
//获取列名 key
titleList.add(key);
}
//表头默认样式
for (int i = 0; i < titleList.size(); i++) {
String title = titleList.get(i);
//如果是被合并单元格则跳过
if (StrUtil.isNotEmpty(title) && title.contains("被合并")) {
continue;
}
Object titleStyle = detail.get(title);
if (titleStyle != null && StrUtil.isNotEmpty(titleStyle.toString())) {
//0:开始行 1:结束行 2:开始列 3.结束列
String[] strs = titleStyle.toString().split(",");
CellRangeAddress region = new CellRangeAddress(Integer.parseInt(strs[0]), Integer.parseInt(strs[1]), Integer.parseInt(strs[2]), Integer.parseInt(strs[3]));
sheet.addMergedRegion(region);
}
}

//表头默认样式
int cellEnd=0;
for (int i = 0; i < titleList.size(); i++) {
String title = titleList.get(i);
//如果是被合并单元格则跳过
if (StrUtil.isNotEmpty(title) && title.contains("被合并")) {
continue;
}
int cellIndex = i;
Object titleStyle = detail.get(title);
if (titleStyle != null && StrUtil.isNotEmpty(titleStyle.toString())) {
//0:开始行 1:结束行 2:开始列 3.结束列
String[] strs = titleStyle.toString().split(",");
//如果有合并则计算单元格位置
cellIndex = Integer.parseInt(strs[2]);
cellEnd =Integer.parseInt(strs[3]);
}else{
if(cellEnd>0){
cellIndex=cellEnd+1;
}
}
Cell cell;
//单元格赋值
cell = row.createCell(cellIndex);
cell.setCellValue(StrUtil.isNotEmpty(title) ? title.split(",")[0] : "");
cell.setCellStyle(style);
sheet.setColumnWidth(i, 6000); //获取列属性
}

}

//处理合并后边框
private static void handleBorder(Row row, CellStyle style, int end) {
Cell cell;
for (int n = 0; n < end; n++) {
cell = row.createCell(n);
cell.setCellStyle(style);
}
}

//定义样式
private static void setCellStyle(Cell cell, Sheet sheet, Row row, HSSFWorkbook wb, Map<String, Object> map, int total) {
CellStyle headStyle = wb.createCellStyle();
Font font2 = wb.createFont();
if (map.get("FONTNAME") != null && StrUtil.isNotEmpty(map.get("FONTNAME").toString())) {
font2.setFontName(map.get("FONTNAME").toString());
} else {
font2.setFontName("宋体");
}
if (map.get("FONT") != null && StrUtil.isNotEmpty(map.get("FONT").toString())) {
font2.setFontHeightInPoints(Short.parseShort(map.get("FONT") + ""));
} else {
font2.setFontHeightInPoints((short) 16);
}
font2.setBold(true);
if (map.get("ALIGNMENT") != null && StrUtil.isNotEmpty(map.get("ALIGNMENT").toString())) {
if ("LEFT".equals(map.get("ALIGNMENT").toString())) {
headStyle.setAlignment(HorizontalAlignment.LEFT);
} else if ("RIGHT".equals(map.get("ALIGNMENT").toString())) {
headStyle.setAlignment(HorizontalAlignment.RIGHT);
} else {
headStyle.setAlignment(HorizontalAlignment.CENTER);
}
} else {
headStyle.setAlignment(HorizontalAlignment.CENTER);
}
headStyle.setVerticalAlignment(VerticalAlignment.CENTER);
headStyle.setBorderBottom(BorderStyle.valueOf((short) 1));
headStyle.setBorderLeft(BorderStyle.valueOf((short) 1));
headStyle.setBorderRight(BorderStyle.valueOf((short) 1));
headStyle.setBorderTop(BorderStyle.valueOf((short) 1));
headStyle.setFont(font2);
//设置样式
cell.setCellStyle(headStyle);
//合并配置
if (map.get("COMBINE_DATAS") != null && StrUtil.isNotEmpty(map.get("COMBINE_DATAS").toString())) {
//0:开始行 1:结束行 2:开始列 3.结束列
String[] strs = map.get("COMBINE_DATAS").toString().split(",");
CellRangeAddress region = new CellRangeAddress(Integer.parseInt(strs[0]), Integer.parseInt(strs[1]), Integer.parseInt(strs[2]), Integer.parseInt(strs[3]));
sheet.addMergedRegion(region);
//处理合并后边框问题 列
handleBorder(row, headStyle, total);
}

}

//样式处理
private static String checkAlignment(Map<String, Object> formatList, String key) {
String alignment = "center";
if (formatList != null && formatList.size() > 0 && formatList.get("ALIGNMENT_RIGHT") != null) {
String[] strs = (String[]) formatList.get("ALIGNMENT_RIGHT");
for (String str : strs) {
if (str.equals(key)) {
alignment = "right";
break;
}
}
}
if ("center".equals(alignment) && formatList != null && formatList.size() > 0 && formatList.get("ALIGNMENT_LEFT") != null) {
String[] strs = (String[]) formatList.get("ALIGNMENT_LEFT");
for (String str : strs) {
if (str.equals(key)) {
alignment = "left";
break;
}
}
}
return alignment;
}
//根据字段自动计算合并数
public static void autoCombineField(List<Map<String, Object>> list, String combineField, String masterField, int startIndex) {
int startComineIndex = 0;
int endComineIndex = - 1;
for (int i = 0; i < list.size(); i++) {
Map<String, Object> map = list.get(i);
String masterCode = map.get(masterField) + "";
String value = map.get(combineField) + "";
if (i < startComineIndex) {
continue;
}
//记录重复数量
int num = 0;
for (int j = i; j < list.size(); j++) {
Map<String, Object> map2 = list.get(j);
String masterCode2 = map2.get(masterField) + "";
String value2 = map2.get(combineField) + "";
if (masterCode.equals(masterCode2) && value.equals(value2)) {
num++;
endComineIndex++;
} else {
if (num == 1) {
//如果只有一个则退回
endComineIndex--;
}
break;
}
}
if (startComineIndex >= endComineIndex||(map.get("BILL_CODE") + "").equals("null") ) {
startComineIndex++;
endComineIndex++;
continue;
}
map.put(combineField + "_COMBINE", (startComineIndex+startIndex) + "," + (endComineIndex+startIndex));
startComineIndex = endComineIndex+1;
}
}

}
posted @ 2022-03-28 10:15  小樊堆码  阅读(42)  评论(0)    收藏  举报