Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
什么會寫Easypoi
以前的以前(歲月真TMD的快)我雖然寫了不少代碼但還是很少寫poi,然后跳到一家公司之后就和業(yè)務(wù)人員聊上了,來這個(gè)需要個(gè)報(bào)表,這個(gè)報(bào)表樣式是這樣的,這個(gè)表頭是這樣的,就這樣我寫了大量的poi代碼,每次都是大量的篇幅,copy to copy,無聊的一逼,然后加入了jeecg,jeecg中有一個(gè)小的工具類,雖然我也不知道是誰寫的,然是可以用注解搞定最簡單的導(dǎo)出,突然豁然開朗,我可以完善,讓我從報(bào)表的苦海當(dāng)中脫離出來,這樣我花了一周的時(shí)間做了第一個(gè)版本支持導(dǎo)入導(dǎo)出放到了jeecg,發(fā)現(xiàn)還是不錯(cuò)的,慢慢的用的人越來越多,我就把這塊獨(dú)立出來了,再然后有人提出了模板,然后就加入了模板功能,提出了word的需求,加入了word的功能,后來工作忙了雖然沒再參與jeecg,但還是一直維持這easypoi的更新,根據(jù)見識的增長也不斷的重構(gòu)這代碼,直到現(xiàn)在
獨(dú)特的功能
小白如何開始
Easypoi 為誰而開發(fā)
都可以使用easypoi
Easypoi的目標(biāo)是什么
Easypoi的目標(biāo)不是替代poi,而是讓一個(gè)不懂導(dǎo)入導(dǎo)出的快速使用poi完成Excel和word的各種操作,而不是看很多api才可以完成這樣工作
1.3 使用
如果不使用spring mvc的便捷福利,直接引入easypoi-base 就可以了,easypoi-annotation
1.4 測試項(xiàng)目
測試這個(gè)事情真不是個(gè)容易的事情
測試項(xiàng)目包括兩塊 Junit 的常見測試和spring 的view測試
1.spring view測試
運(yùn)行application就可以了,訪問界面,然后看到界面
對應(yīng)的代碼在view下面
2.Junit的測試目錄結(jié)構(gòu)如下
目前的測試覆蓋率
2. Excel 注解版
2.1 Excel導(dǎo)入導(dǎo)出
Excel的導(dǎo)入導(dǎo)出是Easypoi的核心功能,前期基本也是圍繞這個(gè)打造的,主要分為三種方式的處理,其中模板和Html目前只支持導(dǎo)出,因?yàn)橹С諱ap.class其實(shí)導(dǎo)入應(yīng)該是怎樣都支持的
下面分別就這三種方式進(jìn)行講解
2.2 注解
注解介紹
easypoi起因就是Excel的導(dǎo)入導(dǎo)出,最初的模板是實(shí)體和Excel的對應(yīng),model--row,filed--col 這樣利用注解我們可以和容易做到excel到導(dǎo)入導(dǎo)出
經(jīng)過一段時(shí)間發(fā)展,現(xiàn)在注解有5個(gè)類分別是
注解中的ID的用法
這個(gè)ID算是一個(gè)比較獨(dú)特的例子,比如
@ExcelTarget("teacherEntity") public class TeacherEntity implements java.io.Serializable { /** name */ @Excel(name="主講老師_teacherEntity,代課老師_absent", orderNum="1", mergeVertical=true,needMerge=true,isImportField="true_major,true_absent") private String name;
這里的@ExcelTarget 表示使用teacherEntity這個(gè)對象是可以針對不同字段做不同處理
同樣的ExcelEntity 和ExcelCollection 都支持這種方式
當(dāng)導(dǎo)出這對象時(shí),name這一列對應(yīng)的是主講老師,而不是代課老師還有很多字段都支持這種做法
@Excel
這個(gè)是必須使用的注解,如果需求簡單只使用這一個(gè)注解也是可以的,涵蓋了常用的Excel需求,需要大家熟悉這個(gè)功能,主要分為基礎(chǔ),圖片處理,時(shí)間處理,合并處理幾塊,name_id是上面講的id用法,這里就不累言了
屬性類型默認(rèn)值功能name
String
null
列名,支持name_id
needMerge
boolean
fasle
是否需要縱向合并單元格(用于含有l(wèi)ist中,單個(gè)的單元格,合并list創(chuàng)建的多個(gè)row)
orderNum
String
"0"
列的排序,支持name_id
replace
String[]
{}
值得替換 導(dǎo)出是{a_id,b_id} 導(dǎo)入反過來
savePath
String
"upload"
導(dǎo)入文件保存路徑,如果是圖片可以填寫,默認(rèn)是upload/className/ IconEntity這個(gè)類對應(yīng)的就是upload/Icon/
type
int
1
導(dǎo)出類型 1 是文本 2 是圖片,3 是函數(shù),10 是數(shù)字 默認(rèn)是文本
width
double
10
列寬
height
double
10
列高,后期打算統(tǒng)一使用@ExcelTarget的height,這個(gè)會被廢棄,注意
isStatistics
boolean
fasle
自動統(tǒng)計(jì)數(shù)據(jù),在追加一行統(tǒng)計(jì),把所有數(shù)據(jù)都和輸出
這個(gè)處理會吞沒異常,請注意這一點(diǎn)
isHyperlink
boolean
false
超鏈接,如果是需要實(shí)現(xiàn)接口返回對象
isImportField
boolean
true
校驗(yàn)字段,看看這個(gè)字段是不是導(dǎo)入的Excel中有,如果沒有說明是錯(cuò)誤的Excel,讀取失敗,支持name_id
exportFormat
String
""
導(dǎo)出的時(shí)間格式,以這個(gè)是否為空來判斷是否需要格式化日期
importFormat
String
""
導(dǎo)入的時(shí)間格式,以這個(gè)是否為空來判斷是否需要格式化日期
format
String
""
時(shí)間格式,相當(dāng)于同時(shí)設(shè)置了exportFormat 和 importFormat
databaseFormat
String
"yyyyMMddHHmmss"
導(dǎo)出時(shí)間設(shè)置,如果字段是Date類型則不需要設(shè)置 數(shù)據(jù)庫如果是string 類型,這個(gè)需要設(shè)置這個(gè)數(shù)據(jù)庫格式,用以轉(zhuǎn)換時(shí)間格式輸出
numFormat
String
""
數(shù)字格式化,參數(shù)是Pattern,使用的對象是DecimalFormat
imageType
int
1
導(dǎo)出類型 1 從file讀取 2 是從數(shù)據(jù)庫中讀取 默認(rèn)是文件 同樣導(dǎo)入也是一樣的
suffix
String
""
文字后綴,如% 90 變成90%
isWrap
boolean
true
是否換行 即支持\n
mergeRely
int[]
{}
合并單元格依賴關(guān)系,比如第二列合并是基于第一列 則{0}就可以了
mergeVertical
boolean
fasle
縱向合并內(nèi)容相同的單元格
fixedIndex
int
-1
對應(yīng)excel的列,忽略名字
isColumnHidden
boolean
false
導(dǎo)出隱藏列
@ExcelTarget
限定一個(gè)到處實(shí)體的注解,以及一些通用設(shè)置,作用于最外面的實(shí)體
屬性類型默認(rèn)值功能value
String
null
定義ID
height
double
10
設(shè)置行高
fontSize
short
11
設(shè)置文字大小
@ExcelEntity
標(biāo)記是不是導(dǎo)出excel 標(biāo)記為實(shí)體類,一遍是一個(gè)內(nèi)部屬性類,標(biāo)記是否繼續(xù)穿透,可以自定義內(nèi)部id
屬性類型默認(rèn)值功能id
String
null
定義ID
@ExcelCollection
一對多的集合注解,用以標(biāo)記集合是否被數(shù)據(jù)以及集合的整體排序
屬性類型默認(rèn)值功能id
String
null
定義ID
name
String
null
定義集合列名,支持nanm_id
orderNum
int
0
排序,支持name_id
type
Class<?>
ArrayList.class
導(dǎo)入時(shí)創(chuàng)建對象使用
@ExcelIgnore
忽略這個(gè)屬性,多使用需循環(huán)引用中,無需多解釋吧^^
2.3 注解導(dǎo)出,導(dǎo)入
2.3.1 對象定義
注解介紹了這么多,大家基本上也了解我們的注解是如何定義Excel的了吧,下面我們來跟著路飛實(shí)戰(zhàn)吧
這天老師吧路飛叫到了辦公室,讓給給老師實(shí)現(xiàn)一個(gè)報(bào)表的需求,就是從教育平臺把某個(gè)班級的人員導(dǎo)出來
需求是,導(dǎo)出我們班的所有學(xué)生的姓名,性別,出生日期,進(jìn)校日期
正巧路飛剛看到Easypo,就打算用Easypoi來實(shí)現(xiàn),實(shí)現(xiàn)方法如下:
首先定義一個(gè)我們導(dǎo)出的對象,為了節(jié)省篇幅,統(tǒng)一忽略getter,setter
public class StudentEntity implements java.io.Serializable { /** * id */ private String id; /** * 學(xué)生姓名 */ @Excel(name="學(xué)生姓名", height=20, width=30, isImportField="true_st") private String name; /** * 學(xué)生性別 */ @Excel(name="學(xué)生性別", replace={ "男_1", "女_2" }, suffix="生", isImportField="true_st") private int sex; @Excel(name="出生日期", databaseFormat="yyyyMMddHHmmss", format="yyyy-MM-dd", isImportField="true_st", width=20) private Date birthday; @Excel(name="進(jìn)校日期", databaseFormat="yyyyMMddHHmmss", format="yyyy-MM-dd") private Date registrationDate; }
這里設(shè)置我們的4列分別是學(xué)生姓名,學(xué)生性別,出生日期,進(jìn)校日期
其中學(xué)生姓名定義了我們的列的行高,學(xué)生性別因?yàn)槲覀兓旧隙际谴嬖跀?shù)據(jù)庫都是數(shù)字所以我們轉(zhuǎn)換下,兩個(gè)日期我們都是進(jìn)行了格式化輸出了,這樣我們就完成了業(yè)務(wù)對我們Excel的樣式需求,后面只有把這個(gè)學(xué)生列表輸出就可以了
生成Excel代碼如下
Workbook workbook=ExcelExportUtil.exportExcel(new ExportParams("計(jì)算機(jī)一班學(xué)生","學(xué)生"), StudentEntity .class, list);
這樣我們就得到的一個(gè)java中的Excel,然后把這個(gè)輸出就得到我們的Excel了https://static.oschina.net/uploads/space/2017/0622/212811_uh7e_1157922.png
2.3.2 集合定義
路飛很快的完成了老師的任務(wù),花了也就是喝杯茶的時(shí)間,就交差了,但過了一會就又被老師叫去了,讓他給出一個(gè)某個(gè)班級選擇選擇某些課的學(xué)生以及對應(yīng)的老師
路飛又很快的想到了Easypoi,其中有一對多的導(dǎo)出,這不正是一對多的體現(xiàn)嗎,然后他繼續(xù)定義實(shí)體:
一個(gè)課程對應(yīng)一個(gè)老師
一個(gè)課程對應(yīng)N個(gè)學(xué)生
課程的實(shí)體
@ExcelTarget("courseEntity") public class CourseEntity implements java.io.Serializable { /** 主鍵 */ private String id; /** 課程名稱 */ @Excel(name="課程名稱", orderNum="1", width=25) private String name; /** 老師主鍵 */ @ExcelEntity(id="absent") private TeacherEntity mathTeacher; @ExcelCollection(name="學(xué)生", orderNum="4") private List<StudentEntity> students; }
教師的實(shí)體
@ExcelTarget("teacherEntity") public class TeacherEntity implements java.io.Serializable { private String id; /** name */ @Excel(name="主講老師_major,代課老師_absent", orderNum="1", isImportField="true_major,true_absent") private String name;
這里在課程這個(gè)實(shí)體里面就完成了一堆多的導(dǎo)出,達(dá)到了我們基礎(chǔ)需求,同時(shí)使用了orderNum對我們的列進(jìn)行了排序,滿足老師的需求,導(dǎo)出代碼如下
Workbook workbook=ExcelExportUtil.exportExcel(new ExportParams("2412312", "測試", "測試"), CourseEntity.class, list);
這樣我們就完成了老師的需求,效果如圖2.3.2-1
但是課程名和代課老師沒有合并,不太美觀
路飛又果斷給課程名稱和代課老師加了needMerge=true的屬性,就可以完成單元格的合并
/** 課程名稱 */ @Excel(name="課程名稱", orderNum="1", width=25,needMerge=true) private String name; //-------------------------------- /** name */ @Excel(name="主講老師_major,代課老師_absent", orderNum="1",needMerge=true, isImportField="true_major,true_absent")
效果如圖2.3.2-2
到這里,路飛就完美的完成了老師的任務(wù),快樂的去交差了
圖2.3.2-1
圖2.3.2-2
2.3.3 圖片的導(dǎo)出
在日常運(yùn)作中不可避免的會遇到圖片的導(dǎo)入導(dǎo)出,這里提供了兩種類型的圖片導(dǎo)出方式
@Excel(name="公司LOGO", type=2 ,width=40 , height=20,imageType=1) private String companyLogo;
@Excel(name="公司LOGO", type=2 ,width=40 , height=20,imageType=1) private byte[] companyLogo;
效果如下
List<CompanyHasImgModel> list; @Before public void initData() { list=new ArrayList<CompanyHasImgModel>(); list.add(new CompanyHasImgModel("百度", "imgs/company/baidu.png", "北京市海淀區(qū)西北旺東路10號院百度科技園1號樓")); list.add(new CompanyHasImgModel("阿里巴巴", "imgs/company/ali.png", "北京市海淀區(qū)西北旺東路10號院百度科技園1號樓")); list.add(new CompanyHasImgModel("Lemur", "imgs/company/lemur.png", "亞馬遜熱帶雨林")); list.add(new CompanyHasImgModel("一眾", "imgs/company/one.png", "山東濟(jì)寧俺家")); } @Test public void exportCompanyImg() throws Exception { File savefile=new File("D:/excel/"); if (!savefile.exists()) { savefile.mkdirs(); } Workbook workbook=ExcelExportUtil.exportExcel(new ExportParams(), CompanyHasImgModel.class, list); FileOutputStream fos=new FileOutputStream("D:/excel/ExcelExportHasImgTest.exportCompanyImg.xls"); workbook.write(fos); fos.close(); }
運(yùn)行效果
2.3.3 -1
2.3.4 Excel導(dǎo)入介紹
有導(dǎo)出就有導(dǎo)入,基于注解的導(dǎo)入導(dǎo)出,配置配置上是一樣的,只是方式反過來而已,比如類型的替換 導(dǎo)出的時(shí)候是1替換成男,2替換成女,導(dǎo)入的時(shí)候則反過來,男變成1 ,女變成2,時(shí)間也是類似
導(dǎo)出的時(shí)候date被格式化成 2017-8-25 ,導(dǎo)入的時(shí)候2017-8-25被格式成date類型
下面說下導(dǎo)入的基本代碼,注解啥的都是上面講過了,這里就不累贅了
@Test public void test2() { ImportParams params=new ImportParams(); params.setTitleRows(1); params.setHeadRows(1); long start=new Date().getTime(); List<MsgClient> list=ExcelImportUtil.importExcel( new File(PoiPublicUtil.getWebRootPath("import/ExcelExportMsgClient.xlsx")), MsgClient.class, params); System.out.println(new Date().getTime() - start); System.out.println(list.size()); System.out.println(ReflectionToStringBuilder.toString(list.get(0))); }
基本是寫法也很簡單,ImportParams 參數(shù)介紹下
屬性類型默認(rèn)值功能titleRows
int
0
表格標(biāo)題行數(shù),默認(rèn)0
headRows
int
1
表頭行數(shù),默認(rèn)1
startRows
int
0
字段真正值和列標(biāo)題之間的距離 默認(rèn)0
keyIndex
int
0
主鍵設(shè)置,如何這個(gè)cell沒有值,就跳過 或者認(rèn)為這個(gè)是list的下面的值
這一列必須有值,不然認(rèn)為這列為無效數(shù)據(jù)
startSheetIndex
int
0
開始讀取的sheet位置,默認(rèn)為0
sheetNum
int
1
上傳表格需要讀取的sheet 數(shù)量,默認(rèn)為1
needSave
boolean
false
是否需要保存上傳的Excel
needVerfiy
boolean
false
是否需要校驗(yàn)上傳的Excel
saveUrl
String
"upload/excelUpload"
保存上傳的Excel目錄,默認(rèn)是 如 TestEntity這個(gè)類保存路徑就是
upload/excelUpload/Test/yyyyMMddHHmss* 保存名稱上傳時(shí)間五位隨機(jī)數(shù)
verifyHanlder
IExcelVerifyHandler
null
校驗(yàn)處理接口,自定義校驗(yàn)
lastOfInvalidRow
int
0
最后的無效行數(shù),不讀的行數(shù)
readRows
int
0
手動控制讀取的行數(shù)
importFields
String[]
null
導(dǎo)入時(shí)校驗(yàn)數(shù)據(jù)模板,是不是正確的Excel
keyMark
String
":"
Key-Value 讀取標(biāo)記,以這個(gè)為Key,后面一個(gè)Cell 為Value,多個(gè)改為ArrayList
readSingleCell
boolean
false
按照Key-Value 規(guī)則讀取全局掃描Excel,但是跳過List讀取范圍提升性能
僅僅支持titleRows + headRows + startRows 以及 lastOfInvalidRow
dataHanlder
IExcelDataHandler
null
數(shù)據(jù)處理接口,以此為主,replace,format都在這后面
2.3.5 Excel導(dǎo)入小功能
2.3.6 圖片的導(dǎo)入
有圖片的導(dǎo)出就有圖片的導(dǎo)入,導(dǎo)入的配置和導(dǎo)出是一樣的,但是需要設(shè)置保存路徑
1.設(shè)置保存路徑saveUrl 默認(rèn)為"upload/excelUpload"
可以手動修改 ImportParams 修改下就可以了
@Test public void test() { try { ImportParams params=new ImportParams(); params.setNeedSave(true); List<CompanyHasImgModel> result=ExcelImportUtil.importExcel( new File(PoiPublicUtil.getWebRootPath("import/imgexcel.xls")), CompanyHasImgModel.class, params); for (int i=0; i < result.size(); i++) { System.out.println(ReflectionToStringBuilder.toString(result.get(i))); } Assert.assertTrue(result.size()==4); } catch (Exception e) { e.printStackTrace(); } } }
導(dǎo)入日志
16:35:43.081 [main] DEBUG c.a.e.e.imports.ExcelImportServer - Excel import start ,class is class cn.afterturn.easypoi.test.entity.img.CompanyHasImgModel 16:35:43.323 [main] DEBUG c.a.e.e.imports.ExcelImportServer - start to read excel by is ,startTime is 1503650143323 16:35:43.344 [main] DEBUG c.a.e.e.imports.ExcelImportServer - end to read excel by is ,endTime is 1503650143344 16:35:43.429 [main] DEBUG c.a.e.e.imports.ExcelImportServer - end to read excel list by pos ,endTime is 1503650143429 cn.afterturn.easypoi.test.entity.img.CompanyHasImgModel@1b083826[companyName=百度,companyLogo=upload/CompanyHasImgModel/pic88273295062.PNG,companyAddr=北京市海淀區(qū)西北旺東路10號院百度科技園1號樓] cn.afterturn.easypoi.test.entity.img.CompanyHasImgModel@105fece7[companyName=阿里巴巴,companyLogo=upload/CompanyHasImgModel/pic22507938183.PNG,companyAddr=北京市海淀區(qū)西北旺東路10號院百度科技園1號樓] cn.afterturn.easypoi.test.entity.img.CompanyHasImgModel@3ec300f1[companyName=Lemur,companyLogo=upload/CompanyHasImgModel/pic86390457892.PNG,companyAddr=亞馬遜熱帶雨林] cn.afterturn.easypoi.test.entity.img.CompanyHasImgModel@482cd91f[companyName=一眾,companyLogo=upload/CompanyHasImgModel/pic69566571093.PNG,companyAddr=山東濟(jì)寧俺家]
2.3.5-1
2.3.7 Excel多Sheet導(dǎo)出
目前單Sheet和單Class的方式比較多,對于多Sheet的方式還是一片空白,這里做一下說明:
導(dǎo)出基本采用ExportParams 這個(gè)對象,進(jìn)行參數(shù)配置;
我們需要進(jìn)行多Sheet導(dǎo)出,那么就需要定義一個(gè)基礎(chǔ)配置對象
public class ExportView { public ExportView(){ } private ExportParams exportParams; private List<?> dataList; private Class<?> cls; public ExportParams getExportParams() { return exportParams; } public void setExportParams(ExportParams exportParams) { this.exportParams=exportParams; } public Class<?> getCls() { return cls; } public void setCls(Class<?> cls) { this.cls=cls; } public List<?> getDataList() { return dataList; } public void setDataList(List<?> dataList) { this.dataList=dataList; } public ExportView(Builder builder) { this.exportParams=builder.exportParams; this.dataList=builder.dataList; this.cls=builder.cls; } public static class Builder { private ExportParams exportParams=null; private List<?> dataList=null; private Class<?> cls=null; public Builder() { } public Builder exportParams(ExportParams exportParams) { this.exportParams=exportParams; return this; } public Builder dataList(List<?> dataList) { this.dataList=dataList; return this; } public Builder cls(Class<?> cls) { this.cls=cls; return this; } public ExportView create() { return new ExportView(this); } } }
對象主要有三個(gè)屬性:
// 該注解配置的導(dǎo)出屬性
這里沒有用泛型,因?yàn)槎郤heet導(dǎo)出時(shí),會引用到不同的注解對象;
定義基礎(chǔ)配置的集合
public class ExportMoreView { private List<ExportView> moreViewList=Lists.newArrayList(); public List<ExportView> getMoreViewList() { return moreViewList; } public void setMoreViewList(List<ExportView> moreViewList) { this.moreViewList=moreViewList; } }
最后在實(shí)現(xiàn)調(diào)用的方法中,對整個(gè)集合進(jìn)行配置和解析
List<Map<String, Object>> exportParamList=Lists.newArrayList(); //該行主要用于獲取業(yè)務(wù)數(shù)據(jù),請根據(jù)具體的情況進(jìn)行修改和調(diào)整 ExportMoreView moreView=this.getBaseTransferService().mergeExportView(templateTypeCode); //迭代導(dǎo)出對象,將對應(yīng)的配置信息寫入到實(shí)際的配置中 for(ExportView view:moreView.getMoreViewList()){ Map<String, Object> valueMap=Maps.newHashMap(); valueMap.put(NormalExcelConstants.PARAMS,view.getExportParams()); valueMap.put(NormalExcelConstants.DATA_LIST,view.getDataList()); valueMap.put(NormalExcelConstants.CLASS,view.getCls()); exportParamList.add(valueMap); } //實(shí)現(xiàn)導(dǎo)出配置 modelMap.put(NormalExcelConstants.FILE_NAME,new DateTime().toString("yyyyMMddHHmmss")); //將轉(zhuǎn)換完成的配置接入到導(dǎo)出中 modelMap.put(NormalExcelConstants.MAP_LIST,exportParamList); return NormalExcelConstants.JEECG_EXCEL_VIEW;
如果不是采用的MVC的方式,請將轉(zhuǎn)換的配置采用以下的方式實(shí)現(xiàn):
參見ExcelExportUtil
2.4 注解變種-更自由的導(dǎo)出
這天老師又把路飛喊道的辦公室,要求路飛導(dǎo)出班級學(xué)生的整體信息
@Excel(name="學(xué)生姓名", height=20, width=30, isImportField="true_st") private String name; @Excel(name="學(xué)生性別", replace={ "男_1", "女_2" }, suffix="生", isImportField="true_st") private int sex; @Excel(name="出生日期", databaseFormat="yyyyMMddHHmmss", format="yyyy-MM-dd", isImportField="true_st", width=20) private Date birthday; @Excel(name="進(jìn)校日期", databaseFormat="yyyyMMddHHmmss", format="yyyy-MM-dd") private Date registrationDate;
路飛飛快的用到上面的學(xué)到的知識搞定了,這這時(shí)有一個(gè)老師把路飛叫去,說想要導(dǎo)出一個(gè)不要出生日期的Excel,感覺用戶需求很無奈,路飛又造兩個(gè)一個(gè)bean,把這個(gè)注解去掉了,來導(dǎo)出
@Excel(name="學(xué)生姓名", height=20, width=30, isImportField="true_st") private String name; @Excel(name="學(xué)生性別", replace={ "男_1", "女_2" }, suffix="生", isImportField="true_st") private int sex; @Excel(name="進(jìn)校日期", databaseFormat="yyyyMMddHHmmss", format="yyyy-MM-dd") private Date registrationDate;
雖然解決了老師的需求,但這個(gè)并不是一個(gè)完美的解決方案,下面介紹一個(gè)更自由的解決方案
注解的導(dǎo)出,規(guī)定我們必須把model寫好,并且注解寫好,每次導(dǎo)出的Excel都是固定的,無法動態(tài)控制導(dǎo)出的列,雖然可以通過id來處理一個(gè)案例,但是自由度遠(yuǎn)遠(yuǎn)不夠,這里介紹個(gè)變種支持,基本支持注解所有的功能
基于List<ExcelExportEntity> 的導(dǎo)出,ExcelExportEntity是注解經(jīng)過處理翻譯成的實(shí)體類,兩者幾乎是一對的,所以如果我們要?jiǎng)討B(tài)自定義導(dǎo)出列,我們只要?jiǎng)討B(tài)拼裝ExcelExportEntity就可以了
下面我們看下這個(gè)類
/** * 如果是MAP導(dǎo)出,這個(gè)是map的key */ private Object key; private double width=10; private double height=10; /** * 圖片的類型,1是文件,2是數(shù)據(jù)庫 */ private int exportImageType=0; /** * 排序順序 */ private int orderNum=0; /** * 是否支持換行 */ private boolean isWrap; /** * 是否需要合并 */ private boolean needMerge; /** * 單元格縱向合并 */ private boolean mergeVertical; /** * 合并依賴 */ private int[] mergeRely; /** * 后綴 */ private String suffix; /** * 統(tǒng)計(jì) */ private boolean isStatistics; private String numFormat; private List<ExcelExportEntity> list;
基本上是和注解對應(yīng)的, List<ExcelExportEntity> list 這個(gè)是對應(yīng)的一對多的導(dǎo)出,相當(dāng)于集合,其他基本上都是和注解保持一致
下面給出正常的demo
public void test() { try { List<ExcelExportEntity> entity=new ArrayList<ExcelExportEntity>(); //構(gòu)造對象等同于@Excel ExcelExportEntity excelentity=new ExcelExportEntity("姓名", "name"); excelentity.setNeedMerge(true); entity.add(excelentity); entity.add(new ExcelExportEntity("性別", "sex")); excelentity=new ExcelExportEntity(null, "students"); List<ExcelExportEntity> temp=new ArrayList<ExcelExportEntity>(); temp.add(new ExcelExportEntity("姓名", "name")); temp.add(new ExcelExportEntity("性別", "sex")); //構(gòu)造List等同于@ExcelCollection excelentity.setList(temp); entity.add(excelentity); List<Map<String, Object>> list=new ArrayList<Map<String, Object>>(); //把我們構(gòu)造好的bean對象放到params就可以了 Workbook workbook=ExcelExportUtil.exportExcel(new ExportParams("測試", "測試"), entity, list); FileOutputStream fos=new FileOutputStream("D:/excel/ExcelExportForMap.tt.xls"); workbook.write(fos); fos.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }
路飛想到了這個(gè)方案,并且用上面做了測試可以完美解決所以他把之前的代碼改為了(代碼有刪減,基本上都是和注解對應(yīng)的)
List<ExcelExportEntity> beanList=new ArrayList<ExcelExportEntity>(); beanList .add(new ExcelExportEntity(new ExcelExportEntity("學(xué)生姓名", "name")); beanList .add(new ExcelExportEntity("學(xué)生性別", "sex")); beanList .add(new ExcelExportEntity("進(jìn)校日期", "registrationDate")); if(needBirthday()){ beanList .add(new ExcelExportEntity("出生日期", "birthday")); } Workbook workbook=ExcelExportUtil.exportExcel(new ExportParams("測試", "測試"), beanList , list);
用同一套代買完美了支持了老師的需求,心滿意足的回宿舍了^^
2.5 Map導(dǎo)入,自由發(fā)揮
這天,老師把路飛叫到辦公室,總是被叫,能者的悲哀啊,讓他臨時(shí)導(dǎo)入一批數(shù)據(jù),到數(shù)據(jù)庫,但是中間需要處理一些字段邏輯沒辦法直接導(dǎo)入到數(shù)據(jù)庫,
這時(shí)路飛首先想到構(gòu)造一個(gè)bean然后標(biāo)記注解,導(dǎo)入處理對象,但是想想一次的對象太過于浪費(fèi),不如用map試試,獲取map處理map也是一樣的
導(dǎo)入的邏輯就變成了
ImportParams params=new ImportParams(); params.setDataHanlder(new MapImportHanlder()); long start=new Date().getTime(); List<Map<String, Object>> list=ExcelImportUtil.importExcel( new File(PoiPublicUtil.getWebRootPath("import/check.xls")), Map.class, params);
導(dǎo)入后,處理每個(gè)map,然后入庫完美的解決了老師的需求,簡單更快捷,和bean導(dǎo)入基礎(chǔ)沒有區(qū)別,省去了bean的構(gòu)造時(shí)間
PS:這個(gè)作者也只是在臨時(shí)方案中或者一次性活當(dāng)中使用,一般還是推薦注解這種方式,擁有更高的代碼閱讀性
!!!測試了時(shí)間的,最好導(dǎo)入使用文本格式,可以獲取時(shí)間格式可能無法獲取
2.6 Excel的樣式自定義
"路飛,來辦公室一趟",就這樣路飛又被叫到了辦公室,這次老師的需求是,想要一個(gè)漂亮點(diǎn)的Excel,希望路飛可以點(diǎn)綴下Excel,思來想去還是需要用poi的style來解決,但是如果每個(gè)都寫style是不是太麻煩,而且Excel的styler數(shù)量是有限制的,這里就需要盡量復(fù)用已經(jīng)創(chuàng)造的style,看看之前的Excel表格,大體上可以分為[標(biāo)題,表頭,表體],那可以說的就是創(chuàng)建一個(gè)接口每次調(diào)用這三個(gè)接口就可以了不說干就干
public interface IExcelExportStyler { /** * 列表頭樣式 * @param headerColor * @return */ public CellStyle getHeaderStyle(short headerColor); /** * 標(biāo)題樣式 * @param color * @return */ public CellStyle getTitleStyle(short color); /** * 獲取樣式方法 * @param Parity * @param entity * @return */ public CellStyle getStyles(boolean Parity, ExcelExportEntity entity); }
實(shí)現(xiàn)類盡量復(fù)用已經(jīng)創(chuàng)建的Styler,切記
這樣路飛先造了一個(gè)帶邊框的styler ,ExcelExportStylerBorderImpl
效果如下
然后路飛又手癢寫了個(gè)帶換行顏色的 ExcelExportStylerColorImpl
效果如下
客官看到這里應(yīng)該就大體理解了我們的實(shí)現(xiàn)方法了吧,
最后路飛實(shí)現(xiàn)了一個(gè)復(fù)雜的按照老師要求的樣式交差了
styler接口用法
上面兩個(gè)表頭和標(biāo)題樣式不用解釋
后面這個(gè)是傳入當(dāng)前列的以及奇偶行,用戶可以根據(jù)需求實(shí)現(xiàn)業(yè)務(wù),包括去掉Excel的小箭頭(也就是設(shè)置數(shù)字為數(shù)字格式的Cell),完成居中,字體等等各式各樣的需求
但是這里無法實(shí)現(xiàn)特別沒的Excel,如果有這種需求可以使用模板來實(shí)現(xiàn),在Excel點(diǎn)點(diǎn)就可以完美實(shí)現(xiàn)
獲取源碼方式 轉(zhuǎn)發(fā)+【關(guān)注】,私信回復(fù)【eypoi】,即可免費(fèi)獲取源碼地址
家好,接下來幾篇文章我將分享基于SSM整合POI實(shí)現(xiàn)Excel的導(dǎo)入導(dǎo)出,并介紹如何采用MVC三層模式體驗(yàn)企業(yè)級JavaWeb應(yīng)用的開發(fā)流程。而這些功能我進(jìn)行了整理并錄制了一套完整的視頻教程,具體的功能列表如下:
1、SSM的整合流程;
2、POI導(dǎo)入導(dǎo)出Excel;
3、增加、刪除、修改、搜索功能。
其中,我對整個(gè)項(xiàng)目進(jìn)行了兩種方式的整合:第一種是采用傳統(tǒng)的往lib目錄丟jar包;第二種是采用maven的方式進(jìn)行整合;而視頻教程中采用的是第一種方式。當(dāng)然啦,這兩種方式的源碼我這里都有!感興趣的童鞋可以私聊!
大家也可以搜索“程序員實(shí)戰(zhàn)基地”,關(guān)于poi實(shí)現(xiàn)Excel導(dǎo)入導(dǎo)出介紹視頻鏈接如下:
http://list.youku.com/albumlist/show/id_51818026.html
在開發(fā)前端這塊大多使用 SpringMVC,EasyPOI 也提供了 對 SpringMVC 的支持;
EasypoiBigExcelExportView 大數(shù)據(jù)量導(dǎo)出
EasypoiMapExcelView map 列表導(dǎo)出
EasypoiPDFTemplateView pdf導(dǎo)出
EasypoiSingleExcelView 注解導(dǎo)出
EasypoiTemplateExcelView 模板導(dǎo)出
EasypoiTemplateWordView word模板導(dǎo)出
MapGraphExcelView 圖表導(dǎo)出
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<%@include file="/WEB-INF/views/head.jsp" %>
</head>
<body>
<span style="color: red">${count}</span>
<!-- 上傳請配置enctype -->
<form action="/import/xlsx" method="post" enctype="multipart/form-data">
<input class="easyui-filebox" name="xlsxFile" data-options="prompt:'選擇一個(gè)文件...'" style="width:80%">
<button class="easyui-linkbutton" type="submit">確定</button>
</form>
</body>
</html>
@Controller
@RequestMapping("/import")
public class ImportController extends BaseController {
@Autowired
private IEmployeeService employeeService;
@Autowired
private IDepartmentService departmentService;
//跳轉(zhuǎn)到導(dǎo)入頁面
@RequestMapping("/index")
public String index(){
return "import";
}
//跳轉(zhuǎn)到導(dǎo)入頁面
@RequestMapping("/xlsx")
public String importXlsx(MultipartFile xlsxFile, HttpServletRequest request, HttpServletResponse response) throws Exception{
ImportParams params=new ImportParams();
params.setTitleRows(1); //注意:這里有兩個(gè)表頭
List<Employee> list=ExcelImportUtil.importExcel(
xlsxFile.getInputStream(),
Employee.class, params);
for (Employee employee : list) {
employee.setPassword("123"); //默認(rèn)密碼123
if(employee.getDepartment()!=null) {
Department department=departmentService.findByName(employee.getDepartment().getName());
employee.setDepartment(department);
}
employeeService.save(employee);
}
return "import";
}
}
自定義驗(yàn)證需要實(shí)現(xiàn)IExcelVerifyHandler接口
*請認(rèn)真填寫需求信息,我們會在24小時(shí)內(nèi)與您取得聯(lián)系。