一、前言
看了例子之后后續(xù)需要更加深入學(xué)習(xí)或者更多理解其他API的話,建議看官方文檔。hutool項(xiàng)目是中國人維護(hù)的,有中文文檔,閱讀起來很方便。apache poi比較底層一點(diǎn),可以更加自由去二次開發(fā)自己所需的功能。
hutool官方文檔
hutool官方gitee
apache poi官方文檔
二、基于hutool poi 實(shí)現(xiàn)導(dǎo)出excel文件到本地
有兩種寫法,可以一開始就為ExcelWriter 指定要寫的文件,另一種是等到要flush的時(shí)候再指定對應(yīng)文件的輸出流。兩種寫法都能實(shí)現(xiàn),不過第二種寫法會顯得更靈活一點(diǎn),可以到后面再慢慢決定要輸出到哪個(gè)文件。
具體實(shí)現(xiàn)邏輯
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class PoiTest {
private int age;
private String name;
public PoiTest() {
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public PoiTest(int age, String name) {
this.age = age;
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PoiTest poiTest = (PoiTest) o;
return age == poiTest.age && Objects.equals(name, poiTest.name);
}
@Override
public int hashCode() {
return Objects.hash(age, name);
}
@Override
public String toString() {
return "PoiTest{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
public static void main(String[] args) throws IOException {
//方式一
File file=new File("E:\\自測資料\\test.xlsx");
file.createNewFile();
ExcelWriter bigWriter = ExcelUtil.getBigWriter();
bigWriter.setDestFile(file);
List<PoiTest> list=new ArrayList<>();
for (int i=0;i<10;i++){
list.add(new PoiTest(18+i,"小明"+i));
}
//將數(shù)據(jù)寫入到ExcelWriter的workbook中
bigWriter.write(list);
//將數(shù)據(jù)寫入excel表
bigWriter.flush();
//關(guān)閉資源
bigWriter.close();
// //方式二
// ExcelWriter bigWriter = ExcelUtil.getBigWriter();
// List<PoiTest> list=new ArrayList<>();
// for (int i=0;i<10;i++){
// list.add(new PoiTest(18+i,"小明"+i));
// }
// bigWriter.write(list);
//
// File file=new File("E:\\自測資料\\test.xlsx");
// file.createNewFile();
// OutputStream outputStream=new FileOutputStream(file);
// bigWriter.flush(outputStream);
// bigWriter.close();
}
}
運(yùn)行結(jié)果
三、基于hutool poi 實(shí)現(xiàn)從本地導(dǎo)入excel文件并轉(zhuǎn)成對象列表
導(dǎo)入可以用map,list,對象來存儲一行記錄,推薦使用對象,方便后期使用。
具體實(shí)現(xiàn)邏輯
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class PoiTest {
private int age;
private String name;
public PoiTest() {
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public PoiTest(int age, String name) {
this.age = age;
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PoiTest poiTest = (PoiTest) o;
return age == poiTest.age && Objects.equals(name, poiTest.name);
}
@Override
public int hashCode() {
return Objects.hash(age, name);
}
@Override
public String toString() {
return "PoiTest{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
public static void main(String[] args) throws IOException {
//方式一
File file=new File("E:\\自測資料\\test.xlsx");
ExcelReader reader = ExcelUtil.getReader(file);
//list中的每個(gè)map對應(yīng)excel表中的一行記錄,excel表第一行(字段名)當(dāng)作key,value就是行記錄對應(yīng)列的值
List<Map<String, Object>> maps = reader.readAll();
for(Map map:maps){
Set<String> set = map.keySet();
for(String key:set){
System.out.println(key+"-->"+map.get(key));
}
System.out.println("-----------");
}
reader.close();
//方式二
// //list返回,內(nèi)部的list,每一個(gè)list代表一行記錄,第一行記錄可以當(dāng)作表頭。
// List<List<Object>> read = reader.read();
// for(List list:read){
// for (Object o:list){
// System.out.println(o);
// }
// System.out.println("-----");
// }
// reader.close();
//方式三
// //根據(jù)表頭名和實(shí)體對象字段名來匹配記錄,并返回實(shí)體列表
// File file=new File("E:\\自測資料\\test.xlsx");
// ExcelReader reader = ExcelUtil.getReader(file);
// /**
// * 第一個(gè)參數(shù)是表頭所在行;第二個(gè)參數(shù)是讀取數(shù)據(jù)開始行,會自動過濾掉表頭行;第三個(gè)參數(shù)是實(shí)體類
// * 如果行記錄所有列都為空,則會跳過該行記錄,不創(chuàng)建對象。只要有一個(gè)列是不為空的,就會創(chuàng)建對象
// **/
// List<PoiTest> read = reader.read(0, 0, PoiTest.class);
// reader.close();
// for(PoiTest poiTest:read){
// System.out.println(poiTest);
// }
}
}
運(yùn)行結(jié)果
map方式返回結(jié)果
list方式返回結(jié)果:(excel中只要行記錄存在,那么空的列會保存為空字符串)
對象列表方式返回結(jié)果:
四、基于apache poi實(shí)現(xiàn)根據(jù)傳入的類將excel的輸入流解析成類對象(excel解析原理)
這個(gè)就是我們?nèi)粘J褂玫膃xcel上傳功能,因此非常有必要掌握原理。
一開始就想過可以通過傳入類名,結(jié)合反射機(jī)制來創(chuàng)建對象,還可以獲取對象的字段并賦值,實(shí)際上思路也確實(shí)大概這樣。我從chat-gpt拿到了一個(gè)解析excel輸入流的demo,不過這個(gè)demo并沒有把excel表第一行當(dāng)作表頭;因此,我在這個(gè)基礎(chǔ)上做了一些修改,實(shí)現(xiàn)了解析excel文件輸入流成對象的工具類方法。(tips:支持的數(shù)據(jù)類型只能是比較基礎(chǔ)的類型,不能是復(fù)雜的對象,復(fù)雜對象先json字符串接收,后面再自己轉(zhuǎn))
動態(tài)代理太酷啦?。?!
當(dāng)然,根據(jù)hutool的工具類就有現(xiàn)成的方法。
ExcelReader reader=getReader(InputStream bookStream)
調(diào)用reader的各種方法就可以實(shí)現(xiàn)解析成各種形式的對象了。
實(shí)體類對象(也就是要解析成的對象)
package com.yumoxuan.pojo;
import java.util.Date;
import java.util.Objects;
public class Person {
private int age;
private String name;
private Date birthday;
private double high;
public Person() {
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public double getHigh() {
return high;
}
public void setHigh(double high) {
this.high = high;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
Double.compare(person.high, high) == 0 &&
Objects.equals(name, person.name) &&
Objects.equals(birthday, person.birthday);
}
@Override
public int hashCode() {
return Objects.hash(age, name, birthday, high);
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
", birthday=" + birthday +
", high=" + high +
'}';
}
}
具體實(shí)現(xiàn)邏輯
import com.yumoxuan.pojo.Person;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.lang.reflect.Field;
public class ExcelParser {
public static <T> List<T> parseExcelFile(InputStream inputStream, Class<T> clazz) {
List<T> objectList = new ArrayList<>();
try (Workbook workbook = new XSSFWorkbook(inputStream)) {
Sheet sheet = workbook.getSheetAt(0); // Assuming the data is in the first sheet
// Get the field names from the class
Field[] fields = clazz.getDeclaredFields();
List<String> objectFieldNameList= Arrays.stream(fields).map(o->o.getName()).collect(Collectors.toList());
//獲取excel第一行作為對象的字段名
List<String> excelFieldNameList = new ArrayList<>();
boolean firstRow = true;
// Iterate over rows in the sheet
for (Row row : sheet) {
T object = clazz.newInstance(); // Create an instance of the class
// Map cell values to object fields
int i = 0;
for (Cell cell : row) {
if (firstRow) {
//獲取excel表的第一行作為字段名
excelFieldNameList.add(cell.getStringCellValue());
continue;
}
if(!objectFieldNameList.contains(excelFieldNameList.get(i))){
//過濾excel中存在,但類中不存在的字段
continue;
}
//注意,這里如果嘗試獲取類里面沒有的字段,會拋異常,因此excel表結(jié)構(gòu)最好協(xié)商定下來。當(dāng)然有了上一步的判斷,這個(gè)問題不會發(fā)生
Field field = clazz.getDeclaredField(excelFieldNameList.get(i));
//開啟編輯權(quán)限
field.setAccessible(true);
Class<?> fieldType = field.getType();
// 這里只需要把可能的數(shù)據(jù)類型都加上就可以了;不過一般也就那么基本數(shù)據(jù)類型加上字符串
if (fieldType == String.class) {
field.set(object, cell.getStringCellValue());
} else if (fieldType == int.class || fieldType == Integer.class) {
field.set(object, (int) cell.getNumericCellValue());
} else if (fieldType == boolean.class || fieldType == Boolean.class) {
field.set(object, cell.getBooleanCellValue());
} else if (fieldType == double.class || fieldType == Double.class) {
field.set(object, cell.getNumericCellValue());
} else if (fieldType == float.class || fieldType == Float.class) {
field.set(object, (float) cell.getNumericCellValue());
} else if (fieldType == Date.class) {
field.set(object, cell.getDateCellValue());
} else {
//暫不支持的類型
}
i++;
//關(guān)閉編輯權(quán)限
field.setAccessible(false);
}
if (!firstRow) {
objectList.add(object);
}
firstRow = false;
}
} catch (IOException | ReflectiveOperationException e) {
e.printStackTrace();
}
return objectList;
}
public static void main(String[] args) throws FileNotFoundException {
File file = new File("F:\\JAVA\\test.xlsx");
InputStream inputStream = new FileInputStream(file);
List<Person> people = ExcelParser.parseExcelFile(inputStream, Person.class);
for (Person person : people) {
System.out.println(person);
}
}
}
excel文件路徑及數(shù)據(jù)
運(yùn)行結(jié)果
Person{age=18, name=‘張三’, birthday=Mon Dec 25 12:13:00 CST 2000, high=163.25}
Person{age=19, name=‘李四’, birthday=Tue Dec 26 12:13:00 CST 2000, high=180.25}文章來源:http://www.zghlxwxcb.cn/news/detail-485967.html
學(xué)無止境,共勉。如果有幫到你,給我點(diǎn)個(gè)贊吧。文章來源地址http://www.zghlxwxcb.cn/news/detail-485967.html
到了這里,關(guān)于hutool poi、apache poi實(shí)現(xiàn)導(dǎo)入導(dǎo)出以及解析excel的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!