EasyExcel教程
本文使用的技術(shù)是Alibaba集團(tuán)開(kāi)源的EasyExcel技術(shù),該技術(shù)是針對(duì)Apache POI技術(shù)的封裝和優(yōu)化,主要解決了POI技術(shù)的耗內(nèi)存問(wèn)題,并且提供了較好的API使用。
- 使用步驟繁瑣
- 動(dòng)態(tài)寫出Excel操作非常麻煩
- 對(duì)于新手來(lái)說(shuō),很難在短時(shí)間內(nèi)上手
- 讀寫時(shí)需要占用較大的內(nèi)容,當(dāng)數(shù)據(jù)量大時(shí)容器發(fā)生OOM
Maven依賴
<!-- easyexcel 依賴 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.2.7</version>
</dependency>
EasyExcel API分析介紹
-
? EasyExcel入口類,用于構(gòu)建開(kāi)始各種操作
-
? ExcelReaderBuilder ExcelWriterBuilder 構(gòu)建出一個(gè) ReadWorkbook WriteWorkbook,可以理解成一個(gè)excel對(duì)象,一個(gè)excel只要構(gòu)建一個(gè)
-
? ExcelReaderSheetBuilder ExcelWriterSheetBuilder 構(gòu)建出一個(gè) ReadSheet WriteSheet對(duì)象,可以理解成excel里面的一頁(yè),每一頁(yè)都要構(gòu)建一個(gè)
-
? ReadListener在每一行讀取完畢后都會(huì)調(diào)用ReadListener來(lái)處理數(shù)據(jù)
-
? WriteHandler在每一個(gè)操作包括創(chuàng)建單元格、創(chuàng)建表格等都會(huì)調(diào)用WriteHandler來(lái)處理數(shù)據(jù)
-
? 所有配置都是繼承的,Workbook的配置會(huì)被Sheet繼承,所以在用EasyExcel設(shè)置參數(shù)的時(shí)候,在EasyExcel…sheet()方法之前作用域是整個(gè)sheet,之后針對(duì)單個(gè)sheet
EasyExcel 注解
-
? ExcelProperty 指定當(dāng)前字段對(duì)應(yīng)excel中的那一列??梢愿鶕?jù)名字或者Index去匹配。當(dāng)然也可以不寫,默認(rèn)第一個(gè)字段就是index=0,以此類推。千萬(wàn)注意,要么全部不寫,要么全部用index,要么全部用名字去匹配。千萬(wàn)別三個(gè)混著用,除非你非常了解源代碼中三個(gè)混著用怎么去排序的。
-
? ExcelIgnore 默認(rèn)所有字段都會(huì)和excel去匹配,加了這個(gè)注解會(huì)忽略該字段
-
? DateTimeFormat 日期轉(zhuǎn)換,用String去接收excel日期格式的數(shù)據(jù)會(huì)調(diào)用這個(gè)注解。里面的value參照java.text.SimpleDateFormat
-
? NumberFormat 數(shù)字轉(zhuǎn)換,用String去接收excel數(shù)字格式的數(shù)據(jù)會(huì)調(diào)用這個(gè)注解。里面的value參照java.text.DecimalFormat
-
? ExcelIgnoreUnannotated 默認(rèn)不加ExcelProperty 的注解的都會(huì)參與讀寫,加了不會(huì)參與
通用參數(shù)
-
? ReadWorkbook,ReadSheet 都會(huì)有的參數(shù),如果為空,默認(rèn)使用上級(jí)。
-
? converter 轉(zhuǎn)換器,默認(rèn)加載了很多轉(zhuǎn)換器。也可以自定義。
-
? readListener 監(jiān)聽(tīng)器,在讀取數(shù)據(jù)的過(guò)程中會(huì)不斷的調(diào)用監(jiān)聽(tīng)器。
-
? headRowNumber 需要讀的表格有幾行頭數(shù)據(jù)。默認(rèn)有一行頭,也就是認(rèn)為第二行開(kāi)始起為數(shù)據(jù)。
-
? head 與clazz二選一。讀取文件頭對(duì)應(yīng)的列表,會(huì)根據(jù)列表匹配數(shù)據(jù),建議使用class。
-
? clazz 與head二選一。讀取文件的頭對(duì)應(yīng)的class,也可以使用注解。如果兩個(gè)都不指定,則會(huì)讀取全部數(shù)據(jù)。
-
? autoTrim 字符串、表頭等數(shù)據(jù)自動(dòng)trim
-
? password 讀的時(shí)候是否需要使用密碼
ReadWorkbook(理解成excel對(duì)象)參數(shù)
-
? excelType 當(dāng)前excel的類型 默認(rèn)會(huì)自動(dòng)判斷
-
? inputStream 與file二選一。讀取文件的流,如果接收到的是流就只用,不用流建議使用file參數(shù)。因?yàn)槭褂昧薸nputStream easyexcel會(huì)幫忙創(chuàng)建臨時(shí)文件,最終還是file
-
? file 與inputStream二選一。讀取文件的文件。
-
? autoCloseStream 自動(dòng)關(guān)閉流。
-
? readCache 默認(rèn)小于5M用 內(nèi)存,超過(guò)5M會(huì)使用 EhCache,這里不建議使用這個(gè)參數(shù)。
ReadSheet(就是excel的一個(gè)Sheet)參數(shù)
-
? sheetNo 需要讀取Sheet的編碼,建議使用這個(gè)來(lái)指定讀取哪個(gè)Sheet
-
? sheetName 根據(jù)名字去匹配Sheet,excel 2003不支持根據(jù)名字去匹配
注解
-
? ExcelProperty index 指定寫到第幾列,默認(rèn)根據(jù)成員變量排序。value指定寫入的名稱,默認(rèn)成員變量的名字,多個(gè)value可以參照快速開(kāi)始中的復(fù)雜頭
-
? ExcelIgnore 默認(rèn)所有字段都會(huì)寫入excel,這個(gè)注解會(huì)忽略這個(gè)字段
-
? DateTimeFormat 日期轉(zhuǎn)換,將Date寫到excel會(huì)調(diào)用這個(gè)注解。里面的value參照java.text.SimpleDateFormat
-
? NumberFormat 數(shù)字轉(zhuǎn)換,用Number寫excel會(huì)調(diào)用這個(gè)注解。里面的value參照java.text.DecimalFormat
-
? ExcelIgnoreUnannotated 默認(rèn)不加ExcelProperty 的注解的都會(huì)參與讀寫,加了不會(huì)參與
參數(shù)
通用參數(shù)
-
? WriteWorkbook,WriteSheet ,WriteTable都會(huì)有的參數(shù),如果為空,默認(rèn)使用上級(jí)。
-
? converter 轉(zhuǎn)換器,默認(rèn)加載了很多轉(zhuǎn)換器。也可以自定義。
-
? writeHandler 寫的處理器。可以實(shí)現(xiàn)WorkbookWriteHandler,SheetWriteHandler,RowWriteHandler,CellWriteHandler,在寫入excel的不同階段會(huì)調(diào)用
-
? relativeHeadRowIndex 距離多少行后開(kāi)始。也就是開(kāi)頭空幾行
-
? needHead 是否導(dǎo)出頭
-
? head 與clazz二選一。寫入文件的頭列表,建議使用class。
-
? clazz 與head二選一。寫入文件的頭對(duì)應(yīng)的class,也可以使用注解。
-
? autoTrim 字符串、表頭等數(shù)據(jù)自動(dòng)trim
WriteWorkbook(理解成excel對(duì)象)參數(shù)
-
? excelType 當(dāng)前excel的類型 默認(rèn)xlsx
-
? outputStream 與file二選一。寫入文件的流
-
? file 與outputStream二選一。寫入的文件
-
? templateInputStream 模板的文件流
-
? templateFile 模板文件
-
? autoCloseStream 自動(dòng)關(guān)閉流。
-
? password 寫的時(shí)候是否需要使用密碼
-
? useDefaultStyle 寫的時(shí)候是否是使用默認(rèn)頭
WriteSheet(就是excel的一個(gè)Sheet)參數(shù)
-
? sheetNo 需要寫入的編碼。默認(rèn)0
-
? sheetName 需要些的Sheet名稱,默認(rèn)同sheetNo
WriteTable(就把excel的一個(gè)Sheet,一塊區(qū)域看一個(gè)table)參數(shù)
- ? tableNo 需要寫入的編碼。默認(rèn)0
EasyExcel用法指南
簡(jiǎn)單的讀取excel文件
public void read() {
String fileName = "demo.xlsx";
// 這里 需要指定讀用哪個(gè)class去讀,然后讀取第一個(gè)sheet 文件流會(huì)自動(dòng)關(guān)閉
// 參數(shù)一:讀取的excel文件路徑
// 參數(shù)二:讀取sheet的一行,將參數(shù)封裝在DemoData實(shí)體類中
// 參數(shù)三:讀取每一行的時(shí)候會(huì)執(zhí)行DemoDataListener監(jiān)聽(tīng)器
EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead();
}
簡(jiǎn)單的寫入excel文件
@Test
public void simpleWrite() {
String fileName = "demo.xlsx";
// 這里 需要指定寫用哪個(gè)class去讀,然后寫到第一個(gè)sheet,名字為模板 然后文件流會(huì)自動(dòng)關(guān)閉
// 如果這里想使用03 則 傳入excelType參數(shù)即可
// 參數(shù)一:寫入excel文件路徑
// 參數(shù)二:寫入的數(shù)據(jù)類型是DemoData
// data()方法是寫入的數(shù)據(jù),結(jié)果是List<DemoData>集合
EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data());
}
Web上傳與下載
/**
excel文件的下載
*/
@GetMapping("download")
public void download(HttpServletResponse response) throws IOException {
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
response.setHeader("Content-disposition", "attachment;filename=demo.xlsx");
EasyExcel.write(response.getOutputStream(), DownloadData.class).sheet("模板").doWrite(data());
}
/**
excel文件的上傳
*/
@PostMapping("upload")
@ResponseBody
public String upload(MultipartFile file) throws IOException {
EasyExcel.read(file.getInputStream(), DemoData.class, new DemoDataListener()).sheet().doRead();
return "success";
}
詳解讀取Excel
對(duì)象模型
// 如果沒(méi)有特殊說(shuō)明,下面的案例將默認(rèn)使用這個(gè)實(shí)體類
public class DemoData {
/**
* 強(qiáng)制讀取第三個(gè) 這里不建議 index 和 name 同時(shí)用,要么一個(gè)對(duì)象只用index,要么一個(gè)對(duì)象只用name去匹配
*/
@ExcelProperty(index = 2)
//我想接收百分比的數(shù)字
@NumberFormat("#.##%")
@ExcelProperty(value="浮點(diǎn)數(shù)標(biāo)題", converter = CustomStringConverter.class)
private Double doubleData;
/**
* 用名字去匹配,這里需要注意,如果名字重復(fù),會(huì)導(dǎo)致只有一個(gè)字段讀取到數(shù)據(jù)
*/
@ExcelProperty(value="字符串標(biāo)題", converter = CustomStringConverter.class)
// converter屬性定義自己的字符串轉(zhuǎn)換器
private String string;
@DateTimeFormat("yyyy年MM月dd日 HH時(shí)mm分ss秒")
@ExcelProperty("日期標(biāo)題")
//這里用string 去接日期才能格式化
private Date date;
}
監(jiān)聽(tīng)器
// 如果沒(méi)有特殊說(shuō)明,下面的案例將默認(rèn)使用這個(gè)監(jiān)聽(tīng)器
public class DemoDataListener extends AnalysisEventListener<DemoData> {
List<DemoData> list = new ArrayList<DemoData>();
/**
* 如果使用了spring,請(qǐng)使用這個(gè)構(gòu)造方法。每次創(chuàng)建Listener的時(shí)候需要把spring管理的類傳進(jìn)來(lái)
*/
public DemoDataListener() {}
/**
* 這個(gè)每一條數(shù)據(jù)解析都會(huì)來(lái)調(diào)用
*
* @param data
* @param context
*/
@Override
public void invoke(DemoData data, AnalysisContext context) {
System.out.println("解析到一條數(shù)據(jù):{}", JSON.toJSONString(data));
list.add(data);
}
/**
* 所有數(shù)據(jù)解析完成了 都會(huì)來(lái)調(diào)用
*
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
System.out.println(JSON.toJSONString(list));
}
}
代碼
@Test
public void simpleRead() {
// 寫法1:
String fileName = "demo.xlsx";
// 這里 需要指定讀用哪個(gè)class去讀,然后讀取第一個(gè)sheet 文件流會(huì)自動(dòng)關(guān)閉
EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead();
// 寫法2:
fileName = "demo.xlsx";
ExcelReader excelReader = EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).build();
ReadSheet readSheet = EasyExcel.readSheet(0).build();
excelReader.read(readSheet);
// 這里千萬(wàn)別忘記關(guān)閉,讀的時(shí)候會(huì)創(chuàng)建臨時(shí)文件,到時(shí)磁盤會(huì)崩的
excelReader.finish();
}
讀取多個(gè)sheet
@Test
public void repeatedRead() {
String fileName = "demo.xlsx";
// 讀取全部sheet
// 這里需要注意 DemoDataListener的doAfterAllAnalysed 會(huì)在每個(gè)sheet讀取完畢后調(diào)用一次。然后所有sheet都會(huì)往同一個(gè)DemoDataListener里面寫
EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).doReadAll();
// 讀取部分sheet
fileName = "demo.xlsx";
ExcelReader excelReader = EasyExcel.read(fileName).build();
// 這里為了簡(jiǎn)單 所以注冊(cè)了 同樣的head 和Listener 自己使用功能必須不同的Listener
// readSheet參數(shù)設(shè)置讀取sheet的序號(hào)
ReadSheet readSheet1 =
EasyExcel.readSheet(0).head(DemoData.class).registerReadListener(new DemoDataListener()).build();
ReadSheet readSheet2 =
EasyExcel.readSheet(1).head(DemoData.class).registerReadListener(new DemoDataListener()).build();
// 這里注意 一定要把sheet1 sheet2 一起傳進(jìn)去,不然有個(gè)問(wèn)題就是03版的excel 會(huì)讀取多次,浪費(fèi)性能
excelReader.read(readSheet1, readSheet2);
// 這里千萬(wàn)別忘記關(guān)閉,讀的時(shí)候會(huì)創(chuàng)建臨時(shí)文件,到時(shí)磁盤會(huì)崩的
excelReader.finish();
}
自定義格式轉(zhuǎn)換
public class CustomStringStringConverter implements Converter<String> {
@Override
public Class supportJavaTypeKey() {
return String.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.STRING;
}
/**
* 這里讀的時(shí)候會(huì)調(diào)用
*
* @param cellData
* NotNull
* @param contentProperty
* Nullable
* @param globalConfiguration
* NotNull
* @return
*/
@Override
public String convertToJavaData(CellData cellData, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) {
return "自定義:" + cellData.getStringValue();
}
/**
* 這里是寫的時(shí)候會(huì)調(diào)用 不用管
*
* @param value
* NotNull
* @param contentProperty
* Nullable
* @param globalConfiguration
* NotNull
* @return
*/
@Override
public CellData convertToExcelData(String value, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) {
return new CellData(value);
}
}
多行頭
@Test
public void complexHeaderRead() {
String fileName = "demo.xlsx";
// 這里 需要指定讀用哪個(gè)class去讀,然后讀取第一個(gè)sheet
EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet()
// 這里可以設(shè)置1,因?yàn)轭^就是一行。如果多行頭,可以設(shè)置其他值。不傳入默認(rèn)1行
.headRowNumber(1).doRead();
}
讀取表頭數(shù)據(jù)
覆蓋監(jiān)聽(tīng)器invokeHeadMap方法
/**
* 這里會(huì)一行行的返回頭
* 監(jiān)聽(tīng)器只需要重寫這個(gè)方法就可以讀取到頭信息
* @param headMap
* @param context
*/
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
LOGGER.info("解析到一條頭數(shù)據(jù):{}", JSON.toJSONString(headMap));
}
異常處理
覆蓋監(jiān)聽(tīng)器onException方法
/**
* 監(jiān)聽(tīng)器實(shí)現(xiàn)這個(gè)方法就可以在讀取數(shù)據(jù)的時(shí)候獲取到異常信息
*/
@Override
public void onException(Exception exception, AnalysisContext context) {
LOGGER.error("解析失敗,但是繼續(xù)解析下一行:{}", exception.getMessage());
// 如果是某一個(gè)單元格的轉(zhuǎn)換異常 能獲取到具體行號(hào)
// 如果要獲取頭的信息 配合invokeHeadMap使用
if (exception instanceof ExcelDataConvertException) {
ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException)exception;
LOGGER.error("第{}行,第{}列解析異常", excelDataConvertException.getRowIndex(),
excelDataConvertException.getColumnIndex());
}
}
導(dǎo)出指定的列
@Test
public void excludeOrIncludeWrite() {
String fileName = "excludeOrIncludeWrite" + System.currentTimeMillis() + ".xlsx";
// 忽略 date 不導(dǎo)出
Set<String> excludeColumnFiledNames = new HashSet<String>();
excludeColumnFiledNames.add("date");
// 這里 需要指定寫用哪個(gè)class去寫,然后寫到第一個(gè)sheet,名字為模板 然后文件流會(huì)自動(dòng)關(guān)閉
EasyExcel.write(fileName, DemoData.class).excludeColumnFiledNames(excludeColumnFiledNames).sheet("忽略date")
.doWrite(data());
fileName = "excludeOrIncludeWrite" + System.currentTimeMillis() + ".xlsx";
// 根據(jù)用戶傳入字段 假設(shè)我們只要導(dǎo)出 date
Set<String> includeColumnFiledNames = new HashSet<String>();
includeColumnFiledNames.add("date");
// 這里 需要指定寫用哪個(gè)class去寫,然后寫到第一個(gè)sheet,名字為模板 然后文件流會(huì)自動(dòng)關(guān)閉
EasyExcel.write(fileName, DemoData.class).includeColumnFiledNames(includeColumnFiledNames).sheet("導(dǎo)出date")
.doWrite(data());
}
調(diào)整指定列順序
public class IndexData {
/**
* 導(dǎo)出的excel第二列和第四列將空置
*/
@ExcelProperty(value = "字符串標(biāo)題", index = 0)
private String string;
@ExcelProperty(value = "日期標(biāo)題", index = 2)
private Date date;
@ExcelProperty(value = "浮點(diǎn)數(shù)標(biāo)題", index = 4)
private Double doubleData;
}
復(fù)雜頭寫入
public class ComplexHeadData {
/**
* 主標(biāo)題 將整合為一個(gè)單元格效果如下:
* —————————————————————————
* | 主標(biāo)題 |
* —————————————————————————
* |字符串標(biāo)題|日期標(biāo)題|數(shù)字標(biāo)題|
* —————————————————————————
*/
@ExcelProperty({"主標(biāo)題", "字符串標(biāo)題"})
private String string;
@ExcelProperty({"主標(biāo)題", "日期標(biāo)題"})
private Date date;
@ExcelProperty({"主標(biāo)題", "數(shù)字標(biāo)題"})
private Double doubleData;
}
前面屬于主標(biāo)題,后面屬于副標(biāo)題
圖片導(dǎo)出
@Data
@ContentRowHeight(200)
@ColumnWidth(200 / 8)
public class ImageData {
// 圖片導(dǎo)出方式有5種
private File file;
private InputStream inputStream;
/**
* 如果string類型 必須指定轉(zhuǎn)換器,string默認(rèn)轉(zhuǎn)換成string,該轉(zhuǎn)換器是官方支持的
*/
@ExcelProperty(converter = StringImageConverter.class)
private String string;
private byte[] byteArray;
/**
* 根據(jù)url導(dǎo)出 版本2.1.1才支持該種模式
*/
private URL url;
}
@Test
public void imageWrite() throws Exception {
String fileName = "imageWrite" + System.currentTimeMillis() + ".xlsx";
// 如果使用流 記得關(guān)閉
InputStream inputStream = null;
try {
List<ImageData> list = new ArrayList<ImageData>();
ImageData imageData = new ImageData();
list.add(imageData);
String imagePath = "converter" + File.separator + "img.jpg";
// 放入五種類型的圖片 根據(jù)實(shí)際使用只要選一種即可
imageData.setByteArray(FileUtils.readFileToByteArray(new File(imagePath)));
imageData.setFile(new File(imagePath));
imageData.setString(imagePath);
inputStream = FileUtils.openInputStream(new File(imagePath));
imageData.setInputStream(inputStream);
imageData.setUrl(new URL(
"https://raw.githubusercontent.com/alibaba/easyexcel/master/src/test/resources/converter/img.jpg"));
EasyExcel.write(fileName, ImageData.class).sheet().doWrite(list);
} finally {
if (inputStream != null) {
inputStream.close();
}
}
}
列寬、行高
@Data
@ContentRowHeight(10)
@HeadRowHeight(20)
@ColumnWidth(25)
public class WidthAndHeightData {
@ExcelProperty("字符串標(biāo)題")
private String string;
@ExcelProperty("日期標(biāo)題")
private Date date;
/**
* 寬度為50,覆蓋上面的寬度25
*/
@ColumnWidth(50)
@ExcelProperty("數(shù)字標(biāo)題")
private Double doubleData;
}
- @HeadRowHeight(value = 35) // 表頭行高
- @ContentRowHeight(value = 25) // 內(nèi)容行高
- @ColumnWidth(value = 50) // 列寬
此外還有,自適應(yīng)寬度,但是這個(gè)不是特別精確
@Test
void contextLoads() {
EasyExcel.write("自適應(yīng).xlsx", Student.class)
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
.sheet()
.doWrite(getData());
}
動(dòng)態(tài)表頭
@Test
public void dynamicHeadWrite() {
String fileName = "dynamicHeadWrite" + System.currentTimeMillis() + ".xlsx";
EasyExcel.write(fileName)
// 這里放入動(dòng)態(tài)頭
.head(head()).sheet("模板")
// 當(dāng)然這里數(shù)據(jù)也可以用 List<List<String>> 去傳入
.doWrite(data());
}
// 動(dòng)態(tài)表頭的數(shù)據(jù)格式List<List<String>>
private List<List<String>> head() {
List<List<String>> list = new ArrayList<List<String>>();
List<String> head0 = new ArrayList<String>();
head0.add("字符串" + System.currentTimeMillis());
List<String> head1 = new ArrayList<String>();
head1.add("數(shù)字" + System.currentTimeMillis());
List<String> head2 = new ArrayList<String>();
head2.add("日期" + System.currentTimeMillis());
list.add(head0);
list.add(head1);
list.add(head2);
return list;
}
合并單元格
@Test
public void mergeWrite() {
String fileName = "mergeWrite" + System.currentTimeMillis() + ".xlsx";
LoopMergeStrategy loopMergeStrategy = new LoopMergeStrategy(2, 0);
EasyExcel.write(fileName, DemoData.class).registerWriteHandler(loopMergeStrategy).sheet("合并單元格")
.doWrite(data());
}
-
每隔2行會(huì)合并 把eachColumn 設(shè)置成 3 也就是我們數(shù)據(jù)的長(zhǎng)度,所以就第一列會(huì)合并。當(dāng)然其他合并策略也可以自己寫
-
這里 需要指定寫用哪個(gè)class去寫,然后寫到第一個(gè)sheet,名字為模板 然后文件流會(huì)自動(dòng)關(guān)閉
web數(shù)據(jù)寫出
@GetMapping("download")
public void download(HttpServletResponse response) throws IOException {
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
// 這里URLEncoder.encode可以防止中文亂碼 當(dāng)然和easyexcel沒(méi)有關(guān)系
String fileName = URLEncoder.encode("數(shù)據(jù)寫出", "UTF-8");
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
EasyExcel.write(response.getOutputStream(), DownloadData.class).sheet("模板").doWrite(data());
}
模板格式導(dǎo)出
如果需要橫向填充只需要模板設(shè)置好就可以。
簡(jiǎn)單的Excel模板
public class FillData {
private String name;
private double number;
// getting setting
}
實(shí)現(xiàn)模板填充
@Test
public void simpleFill() {
String templateFileName = "simple.xlsx";
// 方案1 根據(jù)對(duì)象填充
String fileName = System.currentTimeMillis() + ".xlsx";
// 這里 會(huì)填充到第一個(gè)sheet, 然后文件流會(huì)自動(dòng)關(guān)閉
FillData fillData = new FillData();
fillData.setName("知春秋");
fillData.setNumber(25);
EasyExcel.write(fileName).withTemplate(templateFileName).sheet().doFill(fillData);
// 方案2 根據(jù)Map填充
fileName = System.currentTimeMillis() + ".xlsx";
// 這里 會(huì)填充到第一個(gè)sheet, 然后文件流會(huì)自動(dòng)關(guān)閉
Map<String, Object> map = new HashMap<String, Object>();
map.put("name", "知春秋");
map.put("number", 25);
EasyExcel.write(fileName).withTemplate(templateFileName).sheet().doFill(map);
}
- 模板注意 用{} 來(lái)表示你要用的變量 如果本來(lái)就有"{“,”}" 特殊字符 用"{“,”}"代替
復(fù)雜的填充
使用List集合的方法批量寫入數(shù)據(jù),點(diǎn)表示該參數(shù)是集合
@Test
public void complexFill() {
String templateFileName = "complex.xlsx";
String fileName = System.currentTimeMillis() + ".xlsx";
ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build();
WriteSheet writeSheet = EasyExcel.writerSheet().build();
// 如果數(shù)據(jù)量大 list不是最后一行 參照下一個(gè)
FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
excelWriter.fill(data(), fillConfig, writeSheet);
excelWriter.fill(data(), fillConfig, writeSheet);
// 其他參數(shù)可以使用Map封裝
Map<String, Object> map = new HashMap<String, Object>();
excelWriter.fill(map, writeSheet);
excelWriter.finish();
}
-
模板注意 用{} 來(lái)表示你要用的變量 如果本來(lái)就有"{“,”}" 特殊字符 用"{“,”}"代替文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-804195.html
-
// {} 代表普通變量 {.} 代表是list的變量
-
// 這里注意 入?yún)⒂昧薴orceNewRow 代表在寫入list的時(shí)候不管list下面有沒(méi)有空行 都會(huì)創(chuàng)建一行,然后下面的數(shù)據(jù)往后移動(dòng)。默認(rèn) 是false,會(huì)直接使用下一行,如果沒(méi)有則創(chuàng)建。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-804195.html
-
// forceNewRow 如果設(shè)置了true,有個(gè)缺點(diǎn) 就是他會(huì)把所有的數(shù)據(jù)都放到內(nèi)存了,所以慎用
-
// 簡(jiǎn)單的說(shuō) 如果你的模板有l(wèi)ist,且list不是最后一行,下面還有數(shù)據(jù)需要填充 就必須設(shè)置 forceNewRow=true 但是這個(gè)就會(huì)把所有數(shù)據(jù)放到內(nèi)存 會(huì)很耗內(nèi)存
總結(jié)一個(gè)工具類
public class ExcelUtil {
/**
* 寫出一個(gè) excel 文件到本地
* <br />
* 將類型所有加了 @ExcelProperty 注解的屬性全部寫出
*
* @param fileName 文件名 不要后綴
* @param sheetName sheet名
* @param data 寫出的數(shù)據(jù)
* @param clazz 要寫出數(shù)據(jù)類的Class類型對(duì)象
* @param <T> 寫出的數(shù)據(jù)類型
*/
public static <T> void writeExcel(String fileName, String sheetName, List<T> data, Class<T> clazz) {
writeExcel(null, fileName, sheetName, data, clazz);
}
/**
* 按照指定的屬性名進(jìn)行寫出 一個(gè) excel
*
* @param attrName 指定的屬性名 必須與數(shù)據(jù)類型的屬性名一致
* @param fileName 文件名 不要后綴
* @param sheetName sheet名
* @param data 要寫出的數(shù)據(jù)
* @param clazz 要寫出數(shù)據(jù)類的Class類型對(duì)象
* @param <T> 要寫出的數(shù)據(jù)類型
*/
public static <T> void writeExcel(Set<String> attrName, String fileName, String sheetName, List<T> data, Class<T> clazz) {
fileName = StringUtils.isBlank(fileName) ? "學(xué)生管理系統(tǒng)" : fileName;
sheetName = StringUtils.isBlank(sheetName) ? "sheet0" : sheetName;
try(FileOutputStream fos = new FileOutputStream(fileName)) {
write(fos,attrName,sheetName,data,clazz);
} catch (Exception exception) {
exception.printStackTrace();
}
}
/**
* 讀取 指定格式的 excel文檔
*
* @param fileName 文件名
* @param clazz 數(shù)據(jù)類型的class對(duì)象
* @param <T> 數(shù)據(jù)類型
* @return
*/
public static <T> List<T> readExcel(String fileName, Class<T> clazz) {
return readExcel(fileName, clazz, null);
}
/**
* 取 指定格式的 excel文檔
* 注意一旦傳入自定義監(jiān)聽(tīng)器,則返回的list為空,數(shù)據(jù)需要在自定義監(jiān)聽(tīng)器里面獲取
*
* @param fileName 文件名
* @param clazz 數(shù)據(jù)類型的class對(duì)象
* @param readListener 自定義監(jiān)聽(tīng)器
* @param <T> 數(shù)據(jù)類型
* @return
*/
public static <T> List<T> readExcel(String fileName, Class<T> clazz, ReadListener<T> readListener) {
try(FileInputStream fis = new FileInputStream(fileName)) {
return read(fis,clazz,readListener);
} catch (Exception exception) {
exception.printStackTrace();
}
}
/**
* 導(dǎo)出 一個(gè) excel
* 導(dǎo)出excel所有數(shù)據(jù)
* @param response
* @param fileName 件名 最好為英文,不要后綴名
* @param sheetName sheet名
* @param data 要寫出的數(shù)據(jù)
* @param clazz 要寫出數(shù)據(jù)類的Class類型對(duì)象
* @param <T> 要寫出的數(shù)據(jù)類型
*/
public static <T> void export(HttpServletResponse response, String fileName, String sheetName, List<T> data, Class<T> clazz) {
export(response, null, fileName, sheetName, data, clazz);
}
/**
* 按照指定的屬性名進(jìn)行寫出 一個(gè) excel
*
* @param response
* @param attrName 指定的屬性名 必須與數(shù)據(jù)類型的屬性名一致
* @param fileName 文件名 最好為英文,不要后綴名
* @param sheetName sheet名
* @param data 要寫出的數(shù)據(jù)
* @param clazz 要寫出數(shù)據(jù)類的Class類型對(duì)象
* @param <T> 要寫出的數(shù)據(jù)類型
*/
public static <T> void export(HttpServletResponse response, Set<String> attrName, String fileName, String sheetName, List<T> data, Class<T> clazz) {
fileName = StringUtils.isBlank(fileName) ? "student-system-manager" : fileName;
sheetName = StringUtils.isBlank(sheetName) ? "sheet0" : sheetName;
response.setContentType("application/vnd.ms-excel;charset=utf-8");
response.setCharacterEncoding("utf-8");
response.addHeader("Content-disposition", "attachment;filename=" + fileName + ExcelTypeEnum.XLSX.getValue());
try(OutputStream os = response.getOutputStream()) {
write(os,attrName,sheetName,data,clazz);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 接收一個(gè)excel文件,并且進(jìn)行解析
* 注意一旦傳入自定義監(jiān)聽(tīng)器,則返回的list為空,數(shù)據(jù)需要在自定義監(jiān)聽(tīng)器里面獲取
* @param multipartFile excel文件
* @param clazz 數(shù)據(jù)類型的class對(duì)象
* @param readListener 監(jiān)聽(tīng)器
* @param <T>
* @return
*/
public static <T> List<T> importExcel(MultipartFile multipartFile,Class<T> clazz,ReadListener<T> readListener) {
try(InputStream inputStream = multipartFile.getInputStream()) {
return read(inputStream,clazz,readListener);
} catch (IOException e) {
e.printStackTrace();
}
}
private static <T> void write(OutputStream os, Set<String> attrName, String sheetName, List<T> data, Class<T> clazz) {
ExcelWriterBuilder write = EasyExcel.write(os, clazz);
// 如果沒(méi)有指定要寫出那些屬性數(shù)據(jù),則寫出全部
if (!CollectionUtils.isEmpty(attrName)) {
write.includeColumnFiledNames(attrName);
}
write.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).sheet(sheetName).doWrite(data);
}
private static <T> List<T> read(InputStream in,Class<T> clazz, ReadListener<T> readListener) {
List<T> list = new ArrayList<>();
Optional<ReadListener> optional = Optional.ofNullable(readListener);
EasyExcel.read(in, clazz, optional.orElse(new AnalysisEventListener<T>() {
@Override
public void invoke(T data, AnalysisContext context) {
list.add(data);
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
System.out.println("解析完成");
}
})).sheet().doRead();
return list;
}
}
參考資料
- https://github.com/alibaba/easyexcel/blob/master/docs/API.md 官方api
- https://github.com/alibaba/easyexcel easyexcel github 地址
- https://blog.csdn.net/sinat_32366329/article/details/103109058 easyexcel總結(jié)
- https://alibaba-easyexcel.github.io/index.html 官方示例
到了這里,關(guān)于【Alibaba工具型技術(shù)系列】「EasyExcel技術(shù)專題」實(shí)戰(zhàn)技術(shù)針對(duì)于項(xiàng)目中常用的Excel操作指南的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!