欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Springboot使用POI实现导出Excel文件示例

程序员文章站 2024-03-05 12:12:24
前面讲述了使用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模板文件

创建一个模板文件,如下图:

Springboot使用POI实现导出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使用POI实现导出Excel文件示例

下载地址:springboot_jb51.rar

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。