POI:
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.7.15-SNAPSHOT
com.qx
qx
0.0.1-SNAPSHOT
package1
package1
1.8
org.springframework.boot
spring-boot-starter-web
com.mysql
mysql-connector-j
runtime
org.apache.poi
ooxml-schemas
1.3
com.alibaba
easyexcel
2.2.3
poi-ooxml-schemas
org.apache.poi
org.apache.xmlbeans
xmlbeans
2.6.0
org.projectlombok
lombok
true
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-maven-plugin
org.projectlombok
lombok
spring-milestones
Spring Milestones
https://repo.spring.io/milestone
false
spring-snapshots
Spring Snapshots
https://repo.spring.io/snapshot
false
spring-milestones
Spring Milestones
https://repo.spring.io/milestone
false
spring-snapshots
Spring Snapshots
https://repo.spring.io/snapshot
false
使用一个简单的写入Excle,步骤还是相同,创建文件,创建表,创建行,列。输入数据。
package com.quxiao.controller;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
/**
* @program: package1
* @author: quxiao
* @create: 2023-08-16 15:53
**/
@RestController
@RequestMapping("t")
public class t1 {
@RequestMapping("get")
public String get() throws Exception {
//03版excle
Workbook workbook = new HSSFWorkbook();
//表
Sheet sheet1 = workbook.createSheet("sheet1");
for (int i = 0; i < 10; i++) {
Row row = sheet1.createRow(i);
for (int j = 0; j < 20; j++) {
row.createCell(j).setCellValue(j + "你爹");
}
}
FileOutputStream file = new FileOutputStream("D:\\我的电脑\\fsdownload" + "表.xls");
workbook.write(file);
file.close();
return "ok";
}
}
读取文件,poi还是不好用,它区分2003、2007的Excle
package com.quxiao.controller;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.FileFilter;
import java.io.FileOutputStream;
import java.io.InputStream;
/**
* @program: package1
* @author: quxiao
* @create: 2023-08-16 15:53
**/
@RestController
@RequestMapping("t")
public class t1 {
@RequestMapping("get")
public String get() throws Exception {
//03版excle
Workbook workbook = new HSSFWorkbook();
//表
Sheet sheet1 = workbook.createSheet("sheet1");
for (int i = 0; i < 10; i++) {
Row row = sheet1.createRow(i);
for (int j = 0; j < 20; j++) {
row.createCell(j).setCellValue(j + "你爹");
}
}
FileOutputStream file = new FileOutputStream("D:\\我的电脑\\fsdownload\\" + "表.xlsx");
workbook.write(file);
file.close();
return "ok";
}
@RequestMapping("put")
public String put(MultipartFile filter) throws Exception {
InputStream inputStream = filter.getInputStream();
Workbook workbook = new HSSFWorkbook(inputStream);
// workbook.getNumberOfSheets()
Sheet sheet = workbook.getSheetAt(0);
int ofRows = sheet.getPhysicalNumberOfRows();
for (int j = 0; j < ofRows; j++) {
Row row = sheet.getRow(j);
int oneRowSum = row.getPhysicalNumberOfCells();
//获取第一行
for (int i = 0; i < oneRowSum; i++) {
System.out.print(row.getCell(i).getRichStringCellValue() + " ");
}
System.out.println();
}
inputStream.close();
return "ok";
}
}
使用EasyExcel读:
package com.quxiao.vo;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
@Data
@EqualsAndHashCode
public class DemoData {
@ExcelProperty("编号")
public String id;
@ExcelProperty("姓名")
public String name;
@ExcelProperty("年龄")
public String age;
@ExcelProperty("日期")
public Date time;
public String date;
@Override
public String toString() {
return id + "," + name + "," + age + "," + timeToString(time);
}
public String timeToString(Date t) {
LocalDateTime localDateTime = LocalDateTime.ofInstant(time.toInstant(), ZoneId.of("GMT+8"));
String s = localDateTime.toLocalDate().toString() + " " + localDateTime.toLocalTime().toString();
return s;
}
//上面是实现类,区分开
@PostMapping("upload")
@ResponseBody
public List upload(MultipartFile file) throws IOException {
List list = new ArrayList<>();
//数据,封装类,如何使用
EasyExcel.read(file.getInputStream(), DemoData.class, new ReadListener() {
@Override
public void invoke(DemoData o, AnalysisContext analysisContext) {
//收集
o.setDate(o.timeToString(o.getTime()));
list.add(o);
}
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
}
}).sheet(0).doRead();
return list;
}
}
导出:
@RequestMapping("Ecget")
public String Ecget(HttpServletResponse response) throws Exception {
DemoData d1 = new DemoData("1", "qx1", "10", new Date());
DemoData d2 = new DemoData("2", "qx2", "11", new Date());
DemoData d3 = new DemoData("3", "qx3", "12", new Date());
DemoData d4 = new DemoData("4", "qx4", "13", new Date());
DemoData d5 = new DemoData("5", "qx5", "14", new Date());
ArrayList list = new ArrayList<>();
list.add(d1);
list.add(d2);
list.add(d3);
list.add(d4);
list.add(d5);
export("a.xlsx", response, DemoData.class, list);
return "ok";
}
void export(String fileName, HttpServletResponse response, Class head, List data) throws IOException {
try {
fileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.toString());
response.addHeader("Content-Disposition", "attachment;filename=" + fileName);
WriteCellStyle headWriteCellStyle = new WriteCellStyle();
//设置背景颜色
headWriteCellStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex());
//设置头字体
WriteFont headWriteFont = new WriteFont();
headWriteFont.setFontHeightInPoints((short) 13);
headWriteFont.setBold(true);
headWriteCellStyle.setWriteFont(headWriteFont);
//设置头居中
headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
//内容策略
WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
//设置 水平居中
contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
contentWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
contentWriteCellStyle.setBorderLeft(BorderStyle.THIN);
contentWriteCellStyle.setBorderTop(BorderStyle.THIN);
contentWriteCellStyle.setBorderRight(BorderStyle.THIN);
contentWriteCellStyle.setBorderBottom(BorderStyle.THIN);
HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
EasyExcel.write(response.getOutputStream(), head)
.registerWriteHandler(horizontalCellStyleStrategy)
.sheet("sheet1")
.doWrite(data);
response.getOutputStream().flush();
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
response.getOutputStream().close();
}
}
合并方法:
在实体类中标识需要合并的字段,然后合并过程中调用EasyExcel的回调函数,判断合并
package com.quxiao.vo;
import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
@Data
@EqualsAndHashCode
public class DemoData {
public DemoData(String id, String name, String age, Date time) {
this.id = id;
this.name = name;
this.age = age;
this.time = time;
}
public DemoData() {
}
@CollectCustomMerge(needMerge = true, isPk = true)
@ExcelProperty("编号")
public String id;
@CollectCustomMerge(needMerge = true)
@ExcelProperty("姓名")
public String name;
@ExcelProperty("年龄")
public String age;
@ExcelProperty("日期")
public Date time;
@ExcelIgnore
public String date;
@Override
public String toString() {
return id + "," + name + "," + age + "," + timeToString(time);
}
public String timeToString(Date t) {
LocalDateTime localDateTime = LocalDateTime.ofInstant(time.toInstant(), ZoneId.of("GMT+8"));
String s = localDateTime.toLocalDate().toString() + " " + localDateTime.toLocalTime().toString();
return s;
}
}
注解类:
package com.quxiao.vo; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 自定义注解,用于判断是否需要合并以及合并的主键 * 标记哪些属性需要合并,哪个是主键 */ @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Inherited public @interface CollectCustomMerge { /** * 是否需要合并单元格 */ boolean needMerge() default false; /** * 是否是主键,即该字段相同的行合并 */ boolean isPk() default false; }
创建回调函数类:
package com.quxiao.vo; import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.write.handler.RowWriteHandler; import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; import com.alibaba.excel.write.metadata.holder.WriteTableHolder; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.util.CellRangeAddress; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; /** * @author Administrator * @date 2023/08/02 16:29 **/ public class CustomMergeStrategyTwo implements RowWriteHandler { /** * 主键下标 */ private Integer pkIndex; /** * 需要合并的列的下标集合 */ private List
needMergeColumnIndex = new ArrayList<>(); /** * DTO数据类型 */ private Class> elementType; public CustomMergeStrategyTwo(Class> elementType, int listendIndex) { this.elementType = elementType; this.listendIndex = listendIndex; } @Override public void beforeRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Integer rowIndex, Integer relativeRowIndex, Boolean isHead) { } @Override public void afterRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Integer relativeRowIndex, Boolean isHead) { } int listendIndex = 0; //开始行 int l = 1; @Override public void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Integer relativeRowIndex, Boolean isHead) { // 如果是标题,则直接返回 if (isHead) { return; } // 获取当前sheet Sheet sheet = writeSheetHolder.getSheet(); if (null == pkIndex) { this.lazyInit(writeSheetHolder); } // 不能和标题合并,只能数据行之间合并 if (row.getRowNum() <= 1) { return; } // 获取上一行数据 Row lastRow = sheet.getRow(row.getRowNum() - 1); // 将本行和上一行是同一类型的数据(通过主键字段进行判断),则需要合并 if (lastRow.getCell(pkIndex).getStringCellValue().equalsIgnoreCase(row.getCell(pkIndex).getStringCellValue())) { //判断是不是最后一个,最后一个单独合并哦 if (listendIndex == row.getRowNum()) { for (Integer needMerIndex : needMergeColumnIndex) { CellRangeAddress cellRangeAddress = new CellRangeAddress(l, row.getRowNum(), needMerIndex, needMerIndex); sheet.addMergedRegionUnsafe(cellRangeAddress); } } } else { //开始行,上一行合并 for (Integer needMerIndex : needMergeColumnIndex) { CellRangeAddress cellRangeAddress = new CellRangeAddress(l, row.getRowNum() - 1, needMerIndex, needMerIndex); sheet.addMergedRegionUnsafe(cellRangeAddress); } //更新开始行 l = row.getRowNum(); } } /** * 初始化主键下标和需要合并字段的下标 */ private void lazyInit(WriteSheetHolder writeSheetHolder) { // 获取当前sheet Sheet sheet = writeSheetHolder.getSheet(); // 获取标题行 Row titleRow = sheet.getRow(0); // 获取DTO的类型 Class> eleType = this.elementType; // 获取DTO所有的属性 Field[] fields = eleType.getDeclaredFields(); // 遍历所有的字段,因为是基于DTO的字段来构建excel,所以字段数 >= excel的列数 for (Field theField : fields) { // 获取@ExcelProperty注解,用于获取该字段对应在excel中的列的下标 ExcelProperty easyExcelAnno = theField.getAnnotation(ExcelProperty.class); // 为空,则表示该字段不需要导入到excel,直接处理下一个字段 if (null == easyExcelAnno) { continue; } // 获取自定义的注解,用于合并单元格 CollectCustomMerge collectCustomMerge = theField.getAnnotation(CollectCustomMerge.class); // 没有@CustomMerge注解的默认不合并 if (null == collectCustomMerge) { continue; } for (int index = 0; index < fields.length; index++) { Cell theCell = titleRow.getCell(index); // 当配置为不需要导出时,返回的为null,这里作一下判断,防止NPE if (null == theCell) { continue; } // 将字段和excel的表头匹配上 if (easyExcelAnno.value()[0].equalsIgnoreCase(theCell.getStringCellValue())) { if (collectCustomMerge.isPk()) { pkIndex = index; } if (collectCustomMerge.needMerge()) { needMergeColumnIndex.add(index); } } } } // 没有指定主键,则异常 if (null == this.pkIndex) { throw new IllegalStateException("使用@CustomMerge注解必须指定主键"); } } }
我们在调用时,把行总数传入,这样就可以解决是否最后一行的判断。
@RequestMapping("Ecget2") public String Ecget2(HttpServletResponse response) throws Exception { DemoData d1 = new DemoData("1", "qx1", "11", new Date()); DemoData d2 = new DemoData("1", "qx1", "12", new Date()); DemoData d3 = new DemoData("1", "qx1", "13", new Date()); DemoData d4 = new DemoData("1", "qx1", "14", new Date()); DemoData d5 = new DemoData("1", "qx1", "15", new Date()); DemoData d6 = new DemoData("2", "2", "16", new Date()); DemoData d7 = new DemoData("2", "2", "17", new Date()); DemoData d8 = new DemoData("2", "2", "18", new Date()); DemoData d9 = new DemoData("2", "2", "19", new Date()); DemoData d10 = new DemoData("2", "2", "20", new Date()); DemoData d11 = new DemoData("3", "3", "21", new Date()); ArrayList
list = new ArrayList<>(); list.add(d1); list.add(d2); list.add(d3); list.add(d4); list.add(d5); list.add(d6); list.add(d7); list.add(d8); list.add(d9); list.add(d10); list.add(d11); exportMerge("a.xlsx", response, DemoData.class, list, list.size()); return "ok"; } void export(String fileName, HttpServletResponse response, Class head, List data) throws IOException { try { fileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.toString()); response.addHeader("Content-Disposition", "attachment;filename=" + fileName); WriteCellStyle headWriteCellStyle = new WriteCellStyle(); //设置背景颜色 headWriteCellStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex()); //设置头字体 WriteFont headWriteFont = new WriteFont(); headWriteFont.setFontHeightInPoints((short) 13); headWriteFont.setBold(true); headWriteCellStyle.setWriteFont(headWriteFont); //设置头居中 headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER); //内容策略 WriteCellStyle contentWriteCellStyle = new WriteCellStyle(); //设置 水平居中 contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER); contentWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER); contentWriteCellStyle.setBorderLeft(BorderStyle.THIN); contentWriteCellStyle.setBorderTop(BorderStyle.THIN); contentWriteCellStyle.setBorderRight(BorderStyle.THIN); contentWriteCellStyle.setBorderBottom(BorderStyle.THIN); HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle); EasyExcel.write(response.getOutputStream(), head) .registerWriteHandler(horizontalCellStyleStrategy) .sheet("sheet1") .doWrite(data); response.getOutputStream().flush(); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } catch (IOException e) { throw new RuntimeException(e); } finally { response.getOutputStream().close(); } }