实现一个工作簿中有三张表,其中第三张表为第二张表的详细表,包含相同的信息将会合并单元格,形成压缩文件,给前端下载
以下为前端下载解压打开的效果图:
一、将表头字段排序
使用LinkedList(){{}};目的是为了后面将表头字段有序写入Excel。
// 第一张表:下发总量统计表
String fileNameP1="第一张表(表下面的名称)";
String tableNameP1="统计表(表名)";
List<String>headerP1=new LinkedList<String>(){{
add("序号");add("字段一");add("字段二");add("字段三");add("字段四");add("字段五");add("备注");
}
};
// 第二张表
String fileNameP2="第二张表(表下面的名称)";
String tableNameP2="台账统计表";
List<String>headerP2=new LinkedList<String>(){{
add("序号");add("字段一");add("字段二");add("字段三");add("字段四");add("字段五");add("字段六");
add("字段七");add("字段八");add("备注");add("字段九");add("字段十");
}
};
// 第三张表:条目详细表
String fileNameP3="第三张表(表下面的名称)";
String tableNameP3="详细表";
List<String>headerP3=new LinkedList<String>(){{
add("字段一");add("字段二");add("字段三");add("字段四");add("字段五");add("字段六");
add("字段七");add("字段八");add("字段九");add("字段十");
}
};
二、DownloadServiceImp.java(将数据排序)
Result<List<List<Map<String, Object>>>> dataListP1 = stateService.queryTaskStateTable(参数1, 参数2, null, null, null, null,null,null);//查询第一张表
Result<LinkedList> dataListP2 = stateService.queryTaskStateOfTable(参数1, 参数2, null, null, null, null,null,null,null);//查询第二张表
Result<LinkedList<LinkedList<BatchSupplyLandInfo>>>dataListP3 = stateService.queryDownloadGrant(参数1, 参数2, null, null, null, null, null, null, null);//查询第三张表
List<List<Map<String, Object>>> data1 = dataListP1.getData();
List<Map<String,Object>>dataListP11=data1.get(0);//提取地一张表数据
LinkedList<GrantLandInfo> data2 = dataListP2.getData();//获取第二张表数据
LinkedList<LinkedList<BatchSupplyLandInfo>> data3 = dataListP3.getData();//获取第三张表数据
//以上可根据个人或项目需求,拿数据,这里将不会把获取数据的Service、mapper和XML展示出来
//三张表的数据需进一步有序排列
LinkedList<String> rowData1= new LinkedList<>();
LinkedList<String>rowData2 = new LinkedList<>();
LinkedList<String>rowData3 = new LinkedList<>();
//第一张表:按所给标准表格字段,将数据排序
for (Map<String,Object>data:dataListP11){//遍历第一份数据
List<String>dataTem1 = new LinkedList<>();
for(int i=0;i<data.size();i++){
dataTem1=new LinkedList<String>(){{
add(String.valueOf(Optional.ofNullable(data.get("id")).orElse("")));
add(String.valueOf(Optional.ofNullable(data.get("字段一")).orElse("")));
add(String.valueOf(Optional.ofNullable(data.get("字段二")).orElse("")));
add(String.valueOf(Optional.ofNullable(data.get("字段三")).orElse("")));
add(String.valueOf(Optional.ofNullable(data.get("字段四")).orElse("")));
add(String.valueOf(Optional.ofNullable(data.get("字段五")).orElse("")));
add(String.valueOf(Optional.ofNullable(data.get("bz")).orElse("")));
}};
}
rowData1.add(String.valueOf(dataTem1));
}
//第二张表:按所给标准表格字段,将数据排序
for (int j=0;j<data2.size();j++){
int finalJ = j;
List<String>dataTem2=new LinkedList<String>(){{
add(Optional.ofNullable(data2.get(finalJ).getXh()).orElse(""));
add(Optional.ofNullable(data2.get(finalJ).getzdyName()).orElse(""));
add(Optional.ofNullable(data2.get(finalJ).getzdeName()).orElse(""));
add(Optional.ofNullable(data2.get(finalJ).getzdsName()).orElse(""));
add(Optional.ofNullable(data2.get(finalJ).getzdw()).orElse(""));
add(Optional.ofNullable(data2.get(finalJ).getzdl()).orElse(""));
add(Optional.ofNullable(data2.get(finalJ).getzdq()).orElse(""));
add(Optional.ofNullable(data2.get(finalJ).getzdb()).orElse(""));
add(Optional.ofNullable(data2.get(finalJ).getzdj()).orElse(""));
add(Optional.ofNullable(data2.get(finalJ).getRemark()).orElse(""));
add(Optional.ofNullable(data2.get(finalJ).getzds()).orElse(""));
add(Optional.ofNullable(data2.get(finalJ).getzdsy()).orElse(""));
}};
rowData2.add(String.valueOf(dataTem2));
}
//第三张表:按所给标准表格字段,将数据排序
for (LinkedList<BatchSupplyLandInfo>data:data3){
List<String>dataTem3 = new LinkedList<>();
for(int i=0;i<data.size();i++){
int finalI = i;
dataTem3=new LinkedList<String>(){{
add((Optional.ofNullable(data.get(finalI).getCountyPacName()).orElse("")));
add((Optional.ofNullable(data.get(finalI).getName()).orElse("")));
add((Optional.ofNullable(data.get(finalI).getApprovalNumber()).orElse("")));
add((Optional.ofNullable(data.get(finalI).getBatchSumArea()).orElse("")));//汇总
add((Optional.ofNullable(data.get(finalI).getGrantLandArea()).orElse("")));
add((Optional.ofNullable(data.get(finalI).getProjectName()).orElse("")));
add((Optional.ofNullable(data.get(finalI).getRegulatoryNumber()).orElse("")));
add((Optional.ofNullable(data.get(finalI).getAgreedDate()).orElse("")));
add((Optional.ofNullable(data.get(finalI).getSupplyArea()).orElse("")));
add((Optional.ofNullable(data.get(finalI).getSupplySumArea()).orElse("")));
}};
rowData3.add(String.valueOf(dataTem3));
}
}
//批而未供有三张表
ExcelExp excelExp1=new ExcelExp(fileNameP1, headerP1, rowData1,tableNameP1);
ExcelExp excelExp2=new ExcelExp(fileNameP2, headerP2, rowData2,tableNameP2);
ExcelExp excelExp3=new ExcelExp(fileNameP3, headerP3, rowData3,tableNameP3);
list.add(excelExp1);
list.add(excelExp2);
list.add(excelExp3);
Workbook workbook=POIUtils.exportManySheetExcel(list);
三、ExcelExp.java(存Excel的属性)
package com.hk.dfgag.utils.excelDeal;
import lombok.Data;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@Data
public class ExcelExp {
private String fileName;//sheet的名称
private List<String> handers;//sheet里的Excel名称
private LinkedList<String>dataset;//sheet里的数据集
private String tableName;//表名
public ExcelExp(String fileName, List<String>handers, LinkedList<String> dataset, String tableName){
this.fileName=fileName;
this.handers=handers;
this.dataset=dataset;
this.tableName=tableName;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public List<String> getHanders() {
return handers;
}
public void setHanders(List<String> handers) {
this.handers = handers;
}
public LinkedList<String> getDataset() {
return dataset;
}
public void setDataset(LinkedList<String> dataset) {
this.dataset = dataset;
}
public String getTableName() {
return tableName;
}
public void setTableName(String tableName) {
this.tableName = tableName;
}
}
四、POIUtils.java(最重要的一步,需合并的地方在这里操作)
package com.hk.dfgag.utils.excelDeal;
import java.util.*;
@Component
public class POIUtils {
/**
* @param @param file 导出文件路径
* @param @param mysheets
* @return void
* @throws
* @Title: exportManySheetExcel
* @Description: 可生成单个、多个sheet
*/
public static Workbook exportManySheetExcel(List<ExcelExp> mysheets) {
HSSFWorkbook wb = new HSSFWorkbook();// 创建工作薄
List<ExcelExp> sheets = mysheets;
// 表头样式
HSSFCellStyle style = wb.createCellStyle();
style.setAlignment(HorizontalAlignment.CENTER); // 创建一个居中格式
//设置边框样式
style.setBorderBottom(BorderStyle.THIN); //下边框
style.setBorderLeft(BorderStyle.THIN);//左边框
style.setBorderTop(BorderStyle.THIN);//上边框
style.setBorderRight(BorderStyle.THIN);//右边框
// 字体样式
HSSFFont fontStyle = wb.createFont();
fontStyle.setFontName("微软雅黑");
fontStyle.setFontHeightInPoints((short) 20);
// fontStyle.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
style.setFont(fontStyle);
int flag=0;//标记第几张表
for (ExcelExp excel : sheets) {
flag++;
// 新建一个sheet
HSSFSheet sheet = wb.createSheet(excel.getFileName());// 获取该sheet名称
List<String> handers = excel.getHanders();// 获取sheet的标题名
HSSFRow tableName = sheet.createRow(0);
HSSFCell cellName = tableName.createCell(0);
sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, handers.size()-1));
HSSFCellStyle titleStyle = wb.createCellStyle();
// 设置单元格样式
HSSFFont titleFont = wb.createFont(); // 标题字体
titleFont.setFontHeightInPoints((short) 12); // 字号
titleStyle.setFont(titleFont);
titleStyle.setAlignment(HorizontalAlignment.CENTER);
titleStyle.setVerticalAlignment(VerticalAlignment.CENTER);
titleStyle.setBorderBottom(BorderStyle.THIN); //下边框
titleStyle.setBorderLeft(BorderStyle.THIN);//左边框
titleStyle.setBorderTop(BorderStyle.THIN);//上边框
titleStyle.setBorderRight(BorderStyle.THIN);//右边框
cellName.setCellStyle(style);//加表头的样式
// 设置单元格内容
cellName.setCellValue(excel.getTableName());//
HSSFRow rowFirst = sheet.createRow(1);// 第一个sheet的第一行为标题
// 写标题
for (int i = 0; i < handers.size(); i++) {
// 获取第一行的每个单元格
HSSFCell cell = rowFirst.createCell(i);
// 往单元格里写数据
cell.setCellValue(handers.get(i));
cell.setCellStyle(titleStyle); // 加字段的样式
sheet.setColumnWidth(i, 4000); // 设置每列的列宽
}
// 写数据集
LinkedList<String> dataset = excel.getDataset();
String newData="";
String oldData="";
Cell hc;
int flag1=0;//数据标记
int hNum=0;//行数标记
if(flag==3){//特殊处理第三张表
Map<String, List<Integer>> mergeRowMap = new HashMap<>();//将字段相同的下标和字段做记录
for (int i=0;i<dataset.size();i++){//里面的字段如[[a,b,c,d],[a,b,c,d1],[a,b,c,d1],[a,b,c,d1],[a,b,c,d1],[a,b,c,d1],[a,b,c,d1]……]
newData=dataset.get(i).replaceAll("[\\[\\]]","");//获取的数据为"[a,b,c,d]",现需要将中括号去掉,进行下一步的处理
String[] apNew= newData.split(",");//转为数组,以逗号分割
List<Integer>orDefault=mergeRowMap.getOrDefault(apNew[2],new ArrayList<>());//apNew[2],以c字段作key,记录相同的c字段下标
orDefault.add(i);
mergeRowMap.put(apNew[2],orDefault);
}
for (int i = 0; i < dataset.size(); i++) {
// 创建数据行,将所有数据装进表格中
HSSFRow row = sheet.createRow(i + 2);
String k=dataset.get(i).replaceAll("[\\[\\]]","");
String[] k1= k.split(",");
for(int j=0;j<k1.length;j++){//处理第一二张表数据
Cell cell=row.createCell(j);
cell.setCellValue(k1[j]);
cell.setCellStyle(titleStyle);//加数据的样式
}
}
//开始合并单元格
mergeRowMap.forEach((apNew, rowIndexs) -> {
if (rowIndexs.size() > 1) {
//4个参数,依次为:合并开始行、合并结束行、合并开始列、合并结束列,
//假如有10个相同的字段c,rowIndexs.get(0)指的是第一个相同字段c的下标1,因为有表头和表字段,所以要隔两行,要+2;
//(rowIndexs.size()-1)字段c的size为10,因从0开始,所以要减1,才能rowIndexs.get(); 否则就会溢出,报错,同样要移两行,所以要在整个外面加2,这里的加2真的要注意,不是加在里面。
CellRangeAddress rangeAddress1 = new CellRangeAddress(rowIndexs.get(0)+2, rowIndexs.get(rowIndexs.size()-1)+2, 0, 0);//第一个字段要合并
CellRangeAddress rangeAddress2= new CellRangeAddress(rowIndexs.get(0)+2, rowIndexs.get(rowIndexs.size()-1)+2, 1, 1);//第二个字段要合并
CellRangeAddress rangeAddress3= new CellRangeAddress(rowIndexs.get(0)+2, rowIndexs.get(rowIndexs.size()-1)+2, 2, 2);//第三个字段要合并
CellRangeAddress rangeAddress4= new CellRangeAddress(rowIndexs.get(0)+2, rowIndexs.get(rowIndexs.size()-1)+2, 3, 3);//第四个字段要合并
CellRangeAddress rangeAddress5= new CellRangeAddress(rowIndexs.get(0)+2, rowIndexs.get(rowIndexs.size()-1)+2, 4, 4);
CellRangeAddress rangeAddress6= new CellRangeAddress(rowIndexs.get(0)+2, rowIndexs.get(rowIndexs.size()-1)+2, 9, 9);//最后一个字段要合并
//添加要合并地址到表格
sheet.addMergedRegion(rangeAddress1);
sheet.addMergedRegion(rangeAddress2);
sheet.addMergedRegion(rangeAddress3);
sheet.addMergedRegion(rangeAddress4);
sheet.addMergedRegion(rangeAddress5);
sheet.addMergedRegion(rangeAddress6);
}
});
}else {
for(int i=0;i<dataset.size();i++){
// 创建数据行
HSSFRow row = sheet.createRow(i + 2);
String k=dataset.get(i).replaceAll("[\\[\\]]","");
String[] k1= k.split(",");
for(int j=0;j<k1.length;j++){//处理第一二张表数据
Cell cell=row.createCell(j);
cell.setCellValue(k1[j]);
cell.setCellStyle(titleStyle);
}
}
}
}
return wb;
}
}
五、DownloadServiceImp.java(在第二步完成后,接着写,将Excel压缩下载到前端,并删除本地)
//导出数据到Excel
FileOutputStream fileOutputStream=null;
try{
baseFilePath = xlsFolder + File.separator + RandomUtil.randomString(10);
if (!new File(baseFilePath).exists()) {
new File(baseFilePath).mkdirs();
}
filePath = baseFilePath +"//统计.xls";//为整个文件夹的名称,
//在最外层声明String baseFilePath = "";//文件夹路径
// String filePath = "";//文件路径
fileOutputStream=new FileOutputStream(filePath);
workbook.write(fileOutputStream);
fileOutputStream.flush();
}catch (FileNotFoundException e){
e.printStackTrace();
}catch (IOException e){
e.printStackTrace();
}finally {
if(fileOutputStream!=null){
try{
// 上传成功
fileOutputStream.close();
// 开始压缩
String zipFile=baseFilePath +File.separator + "统计.zip";
try(FileOutputStream fos = new FileOutputStream(zipFile);
ZipOutputStream zos = new ZipOutputStream(fos);) {
File fileToZip = new File(filePath);//将写有数据的文件放入压缩包中
zipFile(fileToZip, fileToZip.getName(), zos);
} catch (Exception ignored) {}
// 将压缩包上传到minio
String downloadUrl = null;
try {
String objectName = "download/"+ CommonUtils.getUUID()+"/统计.zip";
MinioResult minioResult = DownloadServiceImp.minioUtil.upload(new File(zipFile), objectName);
if (minioResult.getCode().equals(200)) {
// 上传成功
downloadUrl = DownloadServiceImp.minioUtil.getDownloadUrl(objectName);
// 插入下载记录
// 插入下载记录
DownloadRecord downloadRecord = new DownloadRecord();
downloadRecord.setCreateTime(new Date());
downloadRecord.setLoginAccount(user.getLoginAccount());
downloadRecord.setPath(objectName);
downLoadMapper.addRecord(downloadRecord);
}
//返回前端后,清除本地文件
FileUtil.del(baseFilePath);
// FileUtil.del(baseFilePath);
} catch (Exception e) {}
if(downloadUrl!=null){
return Result.success(downloadUrl,"导出成功!");//给前端返回downloadUrl,直接Window。open(downloadUrl);即可下载
}
}catch (IOException e){
e.printStackTrace();
}
}
}
在项目中写这个接口之前,我没有做过后端关于表格的操作、也没有写过下载,很多都不懂,遇到很多细小的错,耽误了很多时间,写完之后才发现,其实是简单的,没有必要花这么多时间,所以在这里做个记录,以我小白的角度写一下,可能有点冗余,希望以后会慢慢改进。