Springboot使用POI实现导出Excel文件示例
程序员文章站
2024-03-06 13:33:02
前面讲述了使用poi导出word文件和读取excel文件,这两个例子都相对简单,接下来要讲述的使用poi导出excel文件要复杂得多,内容也会比较长。
创建表头信息...
前面讲述了使用poi导出word文件和读取excel文件,这两个例子都相对简单,接下来要讲述的使用poi导出excel文件要复杂得多,内容也会比较长。
创建表头信息
表头信息用于自动生成表头结构及排序
public class excelheader implements comparable<excelheader>{ /** * excel的标题名称 */ private string title; /** * 每一个标题的顺序 */ private int order; /** * 说对应方法名称 */ private string methodname; public string gettitle() { return title; } public void settitle(string title) { this.title = title; } public int getorder() { return order; } public void setorder(int order) { this.order = order; } public string getmethodname() { return methodname; } public void setmethodname(string methodname) { this.methodname = methodname; } public int compareto(excelheader o) { return order>o.order?1:(order<o.order?-1:0); } public excelheader(string title, int order, string methodname) { super(); this.title = title; this.order = order; this.methodname = methodname; } }
表头信息的annotation
/** * 用来在对象的get方法上加入的annotation,通过该annotation说明某个属性所对应的标题 * created by 钟述林 on 2016/10/29 0:14. */ @retention(retentionpolicy.runtime) public @interface excelresources { /** * 属性的标题名称 * @return */ string title(); /** * 在excel的顺序 * @return */ int order() default 9999; }
创建数据实体
public class webdto { //网站名称 private string name; //网址 private string url; //用户名 private string username; //密码 private string password; //日均访问量 private integer readcount; public webdto(string name, string url, string username, string password, integer readcount) { this.name = name; this.url = url; this.username = username; this.password = password; this.readcount = readcount; } public webdto() {} @override public string tostring() { return "webdto{" + "name='" + name + '\'' + ", url='" + url + '\'' + ", username='" + username + '\'' + ", password='" + password + '\'' + ", readcount=" + readcount + '}'; } @excelresources(title="网站名称",order=1) public string getname() { return name; } public void setname(string name) { this.name = name; } @excelresources(title="网址",order=2) public string geturl() { return url; } public void seturl(string url) { this.url = url; } @excelresources(title="用户名",order=3) public string getusername() { return username; } public void setusername(string username) { this.username = username; } @excelresources(title="密码",order=4) public string getpassword() { return password; } public void setpassword(string password) { this.password = password; } @excelresources(title="日均访问量",order=5) public integer getreadcount() { return readcount; } public void setreadcount(integer readcount) { this.readcount = readcount; } }
注意:这里使用到了@excelresources来自动识别表头信息及序号
获取模板文件的工具类
public class templatefileutil { public static fileinputstream gettemplates(string tempname) throws filenotfoundexception { return new fileinputstream(resourceutils.getfile("classpath:excel-templates/"+tempname)); } }
注意:从这里可以看出,所有的excel模板文件都放在resources/excel-templates/目录下。
模板工具类
通过此类可以自动复制表样式等功能
/** * 该类实现了基于模板的导出 * 如果要导出序号,需要在excel中定义一个标识为sernums * 如果要替换信息,需要传入一个map,这个map中存储着要替换信息的值,在excel中通过#来开头 * 要从哪一行那一列开始替换需要定义一个标识为datas * 如果要设定相应的样式,可以在该行使用styles完成设定,此时所有此行都使用该样式 * 如果使用defaultstyls作为表示,表示默认样式,如果没有defaultstyles使用datas行作为默认样式 * created by 钟述林 393156105@qq.com on 2016/10/28 23:38. */ public class exceltemplate { /** * 数据行标识 */ public final static string data_line = "datas"; /** * 默认样式标识 */ public final static string default_style = "defaultstyles"; /** * 行样式标识 */ public final static string style = "styles"; /** * 插入序号样式标识 */ public final static string ser_num = "sernums"; private static exceltemplate et = new exceltemplate(); private workbook wb; private sheet sheet; /** * 数据的初始化列数 */ private int initcolindex; /** * 数据的初始化行数 */ private int initrowindex; /** * 当前列数 */ private int curcolindex; /** * 当前行数 */ private int currowindex; /** * 当前行对象 */ private row currow; /** * 最后一行的数据 */ private int lastrowindex; /** * 默认样式 */ private cellstyle defaultstyle; /** * 默认行高 */ private float rowheight; /** * 存储某一方所对于的样式 */ private map<integer,cellstyle> styles; /** * 序号的列 */ private int sercolindex; private exceltemplate(){ } public static exceltemplate getinstance() { return et; } /** * 从classpath路径下读取相应的模板文件 * @param path * @return */ public exceltemplate readtemplatebyclasspath(string path) { try { wb = new hssfworkbook(templatefileutil.gettemplates(path)); inittemplate(); } catch (ioexception e) { e.printstacktrace(); throw new runtimeexception("读取模板不存在!请检查"); } return this; } /** * 将文件写到相应的路径下 * @param filepath */ public void writetofile(string filepath) { fileoutputstream fos = null; try { fos = new fileoutputstream(filepath); wb.write(fos); } catch (filenotfoundexception e) { e.printstacktrace(); throw new runtimeexception("写入的文件不存在"); } catch (ioexception e) { e.printstacktrace(); throw new runtimeexception("写入数据失败:"+e.getmessage()); } finally { try { if(fos!=null) fos.close(); } catch (ioexception e) { e.printstacktrace(); } } } /** * 将文件写到某个输出流中 * @param os */ public void wirtetostream(outputstream os) { try { wb.write(os); } catch (ioexception e) { e.printstacktrace(); throw new runtimeexception("写入流失败:"+e.getmessage()); } } /** * 从某个路径来读取模板 * @param path * @return */ public exceltemplate readtemplatebypath(string path) { try { wb = new hssfworkbook(templatefileutil.gettemplates(path)); inittemplate(); } catch (ioexception e) { e.printstacktrace(); throw new runtimeexception("读取模板不存在!请检查"); } return this; } /** * 创建相应的元素,基于string类型 * @param value */ public void createcell(string value) { cell c = currow.createcell(curcolindex); setcellstyle(c); c.setcellvalue(value); curcolindex++; } public void createcell(int value) { cell c = currow.createcell(curcolindex); setcellstyle(c); c.setcellvalue((int)value); curcolindex++; } public void createcell(date value) { cell c = currow.createcell(curcolindex); setcellstyle(c); c.setcellvalue(value); curcolindex++; } public void createcell(double value) { cell c = currow.createcell(curcolindex); setcellstyle(c); c.setcellvalue(value); curcolindex++; } public void createcell(boolean value) { cell c = currow.createcell(curcolindex); setcellstyle(c); c.setcellvalue(value); curcolindex++; } public void createcell(calendar value) { cell c = currow.createcell(curcolindex); setcellstyle(c); c.setcellvalue(value); curcolindex++; } public void createcell(biginteger value) { cell c = currow.createcell(curcolindex); setcellstyle(c); c.setcellvalue(value==null?0:value.intvalue()); curcolindex++; } /** * 设置某个元素的样式 * @param c */ private void setcellstyle(cell c) { if(styles.containskey(curcolindex)) { c.setcellstyle(styles.get(curcolindex)); } else { c.setcellstyle(defaultstyle); } } /** * 创建新行,在使用时只要添加完一行,需要调用该方法创建 */ public void createnewrow() { if(lastrowindex>currowindex&&currowindex!=initrowindex) { sheet.shiftrows(currowindex, lastrowindex, 1,true,true); lastrowindex++; } currow = sheet.createrow(currowindex); currow.setheightinpoints(rowheight); currowindex++; curcolindex = initcolindex; } /** * 插入序号,会自动找相应的序号标示的位置完成插入 */ public void insertser() { int index = 1; row row = null; cell c = null; for(int i=initrowindex;i<currowindex;i++) { row = sheet.getrow(i); c = row.createcell(sercolindex); setcellstyle(c); c.setcellvalue(index++); } } /** * 根据map替换相应的常量,通过map中的值来替换#开头的值 * @param datas */ public void replacefinaldata(map<string,string> datas) { if(datas==null) return; for(row row:sheet) { for(cell c:row) { // if(c.getcelltype()!=cell.cell_type_string) continue; string str = c.getstringcellvalue().trim(); if(str.startswith("#")) { if(datas.containskey(str.substring(1))) { c.setcellvalue(datas.get(str.substring(1))); } } } } } /** * 基于properties的替换,依然也是替换#开始的 * @param prop */ public void replacefinaldata(properties prop) { if(prop==null) return; for(row row:sheet) { for(cell c:row) { // if(c.getcelltype()!=cell.cell_type_string) continue; string str = c.getstringcellvalue().trim(); if(str.startswith("#")) { if(prop.containskey(str.substring(1))) { c.setcellvalue(prop.getproperty(str.substring(1))); } } } } } private void inittemplate() { sheet = wb.getsheetat(0); initconfigdata(); lastrowindex = sheet.getlastrownum(); currow = sheet.createrow(currowindex); } /** * 初始化数据信息 */ private void initconfigdata() { boolean finddata = false; boolean findser = false; for(row row:sheet) { if(finddata) break; for(cell c:row) { // if(c.getcelltype()!=cell.cell_type_string) continue; string str = c.getstringcellvalue().trim(); if(str.equals(ser_num)) { sercolindex = c.getcolumnindex(); findser = true; } if(str.equals(data_line)) { initcolindex = c.getcolumnindex(); initrowindex = row.getrownum(); curcolindex = initcolindex; currowindex = initrowindex; finddata = true; defaultstyle = c.getcellstyle(); rowheight = row.getheightinpoints(); initstyles(); break; } } } if(!findser) { initser(); } } /** * 初始化序号位置 */ private void initser() { for(row row:sheet) { for(cell c:row) { // if(c.getcelltype()!=cell.cell_type_string) continue; string str = c.getstringcellvalue().trim(); if(str.equals(ser_num)) { sercolindex = c.getcolumnindex(); } } } } /** * 初始化样式信息 */ private void initstyles() { styles = new hashmap<integer, cellstyle>(); for(row row:sheet) { for(cell c:row) { // if(c.getcelltype()!=cell.cell_type_string) continue; string str = c.getstringcellvalue().trim(); if(str.equals(default_style)) { defaultstyle = c.getcellstyle(); } if(str.equals(style)) { styles.put(c.getcolumnindex(), c.getcellstyle()); } } } } }
操作工具类
/** * 该类实现了将一组对象转换为excel表格,并且可以从excel表格中读取到一组list对象中 * 该类利用了beanutils框架中的反射完成 * 使用该类的前提,在相应的实体对象上通过excelreources来完成相应的注解 * created by 钟述林 393156105@qq.com on 2016/10/29 0:15. */ public class excelutil { private static excelutil eu = new excelutil(); private excelutil(){} public static excelutil getinstance() { return eu; } /** * 处理对象转换为excel * @param template * @param objs * @param clz * @param isclasspath * @return */ private exceltemplate handlerobj2excel (string template, list objs, class clz, boolean isclasspath) { exceltemplate et = exceltemplate.getinstance(); try { if(isclasspath) { et.readtemplatebyclasspath(template); } else { et.readtemplatebypath(template); } list<excelheader> headers = getheaderlist(clz); collections.sort(headers); //输出标题 et.createnewrow(); for(excelheader eh:headers) { et.createcell(eh.gettitle()); } //输出值 for(object obj:objs) { et.createnewrow(); for(excelheader eh:headers) { // method m = clz.getdeclaredmethod(mn); // object rel = m.invoke(obj); et.createcell(beanutils.getproperty(obj,getmethodname(eh))); } } } catch (illegalaccessexception e) { e.printstacktrace(); } catch (invocationtargetexception e) { e.printstacktrace(); } catch (nosuchmethodexception e) { e.printstacktrace(); } return et; } /** * 根据标题获取相应的方法名称 * @param eh * @return */ private string getmethodname(excelheader eh) { string mn = eh.getmethodname().substring(3); mn = mn.substring(0,1).tolowercase()+mn.substring(1); return mn; } /** * 将对象转换为excel并且导出,该方法是基于模板的导出,导出到流 * @param datas 模板中的替换的常量数据 * @param template 模板路径 * @param os 输出流 * @param objs 对象列表 * @param clz 对象的类型 * @param isclasspath 模板是否在classpath路径下 */ public void exportobj2excelbytemplate(map<string,string> datas, string template, outputstream os, list objs, class clz, boolean isclasspath) { try { exceltemplate et = handlerobj2excel(template, objs, clz, isclasspath); et.replacefinaldata(datas); et.wirtetostream(os); os.flush(); os.close(); } catch (ioexception e) { e.printstacktrace(); } } /** * 将对象转换为excel并且导出,该方法是基于模板的导出,导出到一个具体的路径中 * @param datas 模板中的替换的常量数据 * @param template 模板路径 * @param outpath 输出路径 * @param objs 对象列表 * @param clz 对象的类型 * @param isclasspath 模板是否在classpath路径下 */ public void exportobj2excelbytemplate(map<string,string> datas,string template,string outpath,list objs,class clz,boolean isclasspath) { exceltemplate et = handlerobj2excel(template, objs, clz, isclasspath); et.replacefinaldata(datas); et.writetofile(outpath); } /** * 将对象转换为excel并且导出,该方法是基于模板的导出,导出到流,基于properties作为常量数据 * @param prop 基于properties的常量数据模型 * @param template 模板路径 * @param os 输出流 * @param objs 对象列表 * @param clz 对象的类型 * @param isclasspath 模板是否在classpath路径下 */ public void exportobj2excelbytemplate(properties prop, string template, outputstream os, list objs, class clz, boolean isclasspath) { exceltemplate et = handlerobj2excel(template, objs, clz, isclasspath); et.replacefinaldata(prop); et.wirtetostream(os); } /** * 将对象转换为excel并且导出,该方法是基于模板的导出,导出到一个具体的路径中,基于properties作为常量数据 * @param prop 基于properties的常量数据模型 * @param template 模板路径 * @param outpath 输出路径 * @param objs 对象列表 * @param clz 对象的类型 * @param isclasspath 模板是否在classpath路径下 */ public void exportobj2excelbytemplate(properties prop,string template,string outpath,list objs,class clz,boolean isclasspath) { exceltemplate et = handlerobj2excel(template, objs, clz, isclasspath); et.replacefinaldata(prop); et.writetofile(outpath); } private workbook handleobj2excel(list objs, class clz) { workbook wb = new hssfworkbook(); try { sheet sheet = wb.createsheet(); row r = sheet.createrow(0); list<excelheader> headers = getheaderlist(clz); collections.sort(headers); //写标题 for(int i=0;i<headers.size();i++) { r.createcell(i).setcellvalue(headers.get(i).gettitle()); } //写数据 object obj = null; for(int i=0;i<objs.size();i++) { r = sheet.createrow(i+1); obj = objs.get(i); for(int j=0;j<headers.size();j++) { r.createcell(j).setcellvalue(beanutils.getproperty(obj, getmethodname(headers.get(j)))); } } } catch (illegalaccessexception e) { e.printstacktrace(); } catch (invocationtargetexception e) { e.printstacktrace(); } catch (nosuchmethodexception e) { e.printstacktrace(); } return wb; } /** * 导出对象到excel,不是基于模板的,直接新建一个excel完成导出,基于路径的导出 * @param outpath 导出路径 * @param objs 对象列表 * @param clz 对象类型 */ public void exportobj2excel(string outpath,list objs,class clz) { workbook wb = handleobj2excel(objs, clz); fileoutputstream fos = null; try { fos = new fileoutputstream(outpath); wb.write(fos); } catch (filenotfoundexception e) { e.printstacktrace(); } catch (ioexception e) { e.printstacktrace(); } finally { try { if(fos!=null) fos.close(); } catch (ioexception e) { e.printstacktrace(); } } } /** * 导出对象到excel,不是基于模板的,直接新建一个excel完成导出,基于流 * @param os 输出流 * @param objs 对象列表 * @param clz 对象类型 */ public void exportobj2excel(outputstream os,list objs,class clz) { try { workbook wb = handleobj2excel(objs, clz); wb.write(os); } catch (ioexception e) { e.printstacktrace(); } } /** * 从类路径读取相应的excel文件到对象列表 * @param path 类路径下的path * @param clz 对象类型 * @param readline 开始行,注意是标题所在行 * @param tailline 底部有多少行,在读入对象时,会减去这些行 * @return */ public list<object> readexcel2objsbyclasspath(string path,class clz,int readline,int tailline) { workbook wb = null; try { wb = new hssfworkbook(templatefileutil.gettemplates(path)); return handlerexcel2objs(wb, clz, readline,tailline); } catch (ioexception e) { e.printstacktrace(); } return null; } /** * 从文件路径读取相应的excel文件到对象列表 * @param path 文件路径下的path * @param clz 对象类型 * @param readline 开始行,注意是标题所在行 * @param tailline 底部有多少行,在读入对象时,会减去这些行 * @return */ public list<object> readexcel2objsbypath(string path,class clz,int readline,int tailline) { workbook wb = null; try { wb = new hssfworkbook(templatefileutil.gettemplates(path)); return handlerexcel2objs(wb, clz, readline,tailline); } catch (ioexception e) { e.printstacktrace(); } return null; } /** * 从类路径读取相应的excel文件到对象列表,标题行为0,没有尾行 * @param path 路径 * @param clz 类型 * @return 对象列表 */ public list<object> readexcel2objsbyclasspath(string path,class clz) { return this.readexcel2objsbyclasspath(path, clz, 0,0); } /** * 从文件路径读取相应的excel文件到对象列表,标题行为0,没有尾行 * @param path 路径 * @param clz 类型 * @return 对象列表 */ public list<object> readexcel2objsbypath(string path,class clz) { return this.readexcel2objsbypath(path, clz,0,0); } private string getcellvalue(cell c) { string o = null; switch (c.getcelltype()) { case cell.cell_type_blank: o = ""; break; case cell.cell_type_boolean: o = string.valueof(c.getbooleancellvalue()); break; case cell.cell_type_formula: o = string.valueof(c.getcellformula()); break; case cell.cell_type_numeric: o = string.valueof(c.getnumericcellvalue()); break; case cell.cell_type_string: o = c.getstringcellvalue(); break; default: o = null; break; } return o; } private list<object> handlerexcel2objs(workbook wb,class clz,int readline,int tailline) { sheet sheet = wb.getsheetat(0); list<object> objs = null; try { row row = sheet.getrow(readline); objs = new arraylist<object>(); map<integer,string> maps = getheadermap(row, clz); if(maps==null||maps.size()<=0) throw new runtimeexception("要读取的excel的格式不正确,检查是否设定了合适的行"); for(int i=readline+1;i<=sheet.getlastrownum()-tailline;i++) { row = sheet.getrow(i); object obj = clz.newinstance(); for(cell c:row) { int ci = c.getcolumnindex(); string mn = maps.get(ci).substring(3); mn = mn.substring(0,1).tolowercase()+mn.substring(1); beanutils.copyproperty(obj,mn, this.getcellvalue(c)); } objs.add(obj); } } catch (instantiationexception e) { e.printstacktrace(); } catch (illegalaccessexception e) { e.printstacktrace(); } catch (invocationtargetexception e) { e.printstacktrace(); } return objs; } private list<excelheader> getheaderlist(class clz) { list<excelheader> headers = new arraylist<excelheader>(); method[] ms = clz.getdeclaredmethods(); for(method m:ms) { string mn = m.getname(); if(mn.startswith("get")) { if(m.isannotationpresent(excelresources.class)) { excelresources er = m.getannotation(excelresources.class); headers.add(new excelheader(er.title(),er.order(),mn)); } } } return headers; } private map<integer,string> getheadermap(row titlerow,class clz) { list<excelheader> headers = getheaderlist(clz); map<integer,string> maps = new hashmap<integer, string>(); for(cell c:titlerow) { string title = c.getstringcellvalue(); for(excelheader eh:headers) { if(eh.gettitle().equals(title.trim())) { maps.put(c.getcolumnindex(), eh.getmethodname().replace("get","set")); break; } } } return maps; } }
excel模板文件
创建一个模板文件,如下图:
poi导出excel的模板文件
测试类
@springboottest @runwith(springrunner.class) public class exportexceltest { @test public void test() throws exception { list<webdto> list = new arraylist<webdto>(); list.add(new webdto("知识林", "http://www.zslin.com", "admin", "111111", 555)); list.add(new webdto("权限系统", "http://basic.zslin.com", "admin", "111111", 111)); list.add(new webdto("校园网", "http://school.zslin.com", "admin", "222222", 333)); map<string, string> map = new hashmap<string, string>(); map.put("title", "网站信息表"); map.put("total", list.size()+" 条"); map.put("date", getdate()); excelutil.getinstance().exportobj2excelbytemplate(map, "web-info-template.xls", new fileoutputstream("d:/temp/out.xls"), list, webdto.class, true); } private string getdate() { simpledateformat sdf = new simpledateformat("yyyy年mm月dd日"); return sdf.format(new date()); } }
执行测试方法后,查看d:/temp/out.xls文件后可以看到如下图的内容:
poi导出excel结果图
下载地址:springboot_jb51.rar
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
推荐阅读
-
Springboot使用POI实现导出Excel文件示例
-
Springboot使用POI实现导出Excel文件示例
-
Java POI实现将导入Excel文件的示例代码
-
poi(页面文件导出到excel)实现文件下载
-
Java POI实现将导入Excel文件的示例代码
-
原生PHP实现导出csv格式Excel文件的方法示例【附源码下载】
-
PHP封装的svn类使用内置svn函数实现根据svn版本号导出相关文件示例
-
java使用POI实现excel文件的导入和导出(通用方法)
-
Laravel 5使用Laravel Excel实现Excel/CSV文件导入导出的功能详解
-
大数据量Excel导出使用SXSSFWorkbook对象临时文件位置实现自定义配置