评论

收藏

[Java] Java利用POI读取、写入Excel的方法指南

编程语言 编程语言 发布于:2021-09-18 15:21 | 阅读数:207 | 评论:0

这篇文章主要给大家介绍了关于Java利用POI读取、写入Excel的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
前言
apache poi [1] 是用java编写的免费开源的跨平台的 java api,apache poi提供api给java程式对microsoft office格式档案读和写的功能。poi为“poor obfuscation implementation”的首字母缩写,意为“简洁版的模糊实现”。
做项目时经常有通过程序读取excel数据,或是创建新的excel并写入数据的需求;
网上很多经验教程里使用的poi版本都比较老了,一些api在新版里已经废弃,这里基于最新的apache poi 4.0.1版本来总结一下整个读取和写入excel的过程,希望能帮助到需要的人 ^_^
1. 准备工作
1.1 在项目中引入apache poi相关类库
引入 apache poi 和 apache poi-ooxml 这两个类库,maven坐标如下:
<dependency>
<groupid>org.apache.poi</groupid>
<artifactid>poi</artifactid>
<version>4.0.1</version>
</dependency>
<dependency>
<groupid>org.apache.poi</groupid>
<artifactid>poi-ooxml</artifactid>
<version>4.0.1</version>
</dependency>
2. 读取或写入excel数据
2.1 示例程序结构说明
简单说明一下示例程序的整体结构:

  • excelreader.java是实现读取excel数据功能的类;
  • excelwriter.java是创建新的excel并向其中写入数据的类;
  • exceldatavo.java封装了读取或写入时每一“行”的数据;
  • maintest.java是示例程序的入口类,其中演示了读取和写入excel数据的整个过程;
2.2 读取数据
示例程序需要从桌面读取 readexample.xlsx 内的数据,readexample.xlsx 的内容如下:
DSC0000.png

读取excel时主要调用excelreader.java类来读取和解析excel的具体内容,这里以读取系统文件的形式演示读取过程:(兼容 xls 和 xlsx)
2.2.1 主程序入口类代码:
/**
 * author: dreamer-1
 * date: 2019-03-01
 * time: 10:13
 * description: 示例程序入口类
 */
public class maintest {
 
 public static void main(string[] args) {
 // 设定excel文件所在路径
 string excelfilename = "/users/dreamer-1/desktop/myblog/java解析excel/readexample.xlsx";
 // 读取excel文件内容
 list<exceldatavo> readresult = excelreader.readexcel(excelfilename);
 
 // todo 进行业务操作
 }
 
}
读取和写入时封装每一“行”数据的exceldatavo.java代码如下:
/**
 * author: dreamer-1
 * date: 2019-03-01
 * time: 11:33
 * description: 读取excel时,封装读取的每一行的数据
 */
public class exceldatavo {
 
 /**
 * 姓名
 */
 private string name;
 
 /**
 * 年龄
 */
 private integer age;
 
 /**
 * 居住地
 */
 private string location;
 
 /**
 * 职业
 */
 private string job;
 
 public string getname() {
 return name;
 }
 
 public void setname(string name) {
 this.name = name;
 }
 
 public integer getage() {
 return age;
 }
 
 public void setage(integer age) {
 this.age = age;
 }
 
 public string getlocation() {
 return location;
 }
 
 public void setlocation(string location) {
 this.location = location;
 }
 
 public string getjob() {
 return job;
 }
 
 public void setjob(string job) {
 this.job = job;
 }
}
2.2.2 excel解析类的代码:
/**
 * author: dreamer-1
 * date: 2019-03-01
 * time: 10:21
 * description: 读取excel内容
 */
public class excelreader {
 
 private static logger logger = logger.getlogger(excelreader.class.getname()); // 日志打印类
 
 private static final string xls = "xls";
 private static final string xlsx = "xlsx";
 
 /**
 * 根据文件后缀名类型获取对应的工作簿对象
 * @param inputstream 读取文件的输入流
 * @param filetype 文件后缀名类型(xls或xlsx)
 * @return 包含文件数据的工作簿对象
 * @throws ioexception
 */
 public static workbook getworkbook(inputstream inputstream, string filetype) throws ioexception {
 workbook workbook = null;
 if (filetype.equalsignorecase(xls)) {
  workbook = new hssfworkbook(inputstream);
 } else if (filetype.equalsignorecase(xlsx)) {
  workbook = new xssfworkbook(inputstream);
 }
 return workbook;
 }
 
 /**
 * 读取excel文件内容
 * @param filename 要读取的excel文件所在路径
 * @return 读取结果列表,读取失败时返回null
 */
 public static list<exceldatavo> readexcel(string filename) {
 
 workbook workbook = null;
 fileinputstream inputstream = null;
 
 try {
  // 获取excel后缀名
  string filetype = filename.substring(filename.lastindexof(".") + 1, filename.length());
  // 获取excel文件
  file excelfile = new file(filename);
  if (!excelfile.exists()) {
  logger.warning("指定的excel文件不存在!");
  return null;
  }
 
  // 获取excel工作簿
  inputstream = new fileinputstream(excelfile);
  workbook = getworkbook(inputstream, filetype);
 
  // 读取excel中的数据
  list<exceldatavo> resultdatalist = parseexcel(workbook);
 
  return resultdatalist;
 } catch (exception e) {
  logger.warning("解析excel失败,文件名:" + filename + " 错误信息:" + e.getmessage());
  return null;
 } finally {
  try {
  if (null != workbook) {
   workbook.close();
  }
  if (null != inputstream) {
   inputstream.close();
  }
  } catch (exception e) {
  logger.warning("关闭数据流出错!错误信息:" + e.getmessage());
  return null;
  }
 }
 }
 
 /**
 * 解析excel数据
 * @param workbook excel工作簿对象
 * @return 解析结果
 */
 private static list<exceldatavo> parseexcel(workbook workbook) {
 list<exceldatavo> resultdatalist = new arraylist<>();
 // 解析sheet
 for (int sheetnum = 0; sheetnum < workbook.getnumberofsheets(); sheetnum++) {
  sheet sheet = workbook.getsheetat(sheetnum);
 
  // 校验sheet是否合法
  if (sheet == null) {
  continue;
  }
 
  // 获取第一行数据
  int firstrownum = sheet.getfirstrownum();
  row firstrow = sheet.getrow(firstrownum);
  if (null == firstrow) {
  logger.warning("解析excel失败,在第一行没有读取到任何数据!");
  }
 
  // 解析每一行的数据,构造数据对象
  int rowstart = firstrownum + 1;
  int rowend = sheet.getphysicalnumberofrows();
  for (int rownum = rowstart; rownum < rowend; rownum++) {
  row row = sheet.getrow(rownum);
 
  if (null == row) {
   continue;
  }
 
  exceldatavo resultdata = convertrowtodata(row);
  if (null == resultdata) {
   logger.warning("第 " + row.getrownum() + "行数据不合法,已忽略!");
   continue;
  }
  resultdatalist.add(resultdata);
  }
 }
 
 return resultdatalist;
 }
 
 /**
 * 将单元格内容转换为字符串
 * @param cell
 * @return
 */
 private static string convertcellvaluetostring(cell cell) {
 if(cell==null){
  return null;
 }
 string returnvalue = null;
 switch (cell.getcelltype()) {
  case numeric: //数字
  double doublevalue = cell.getnumericcellvalue();
 
  // 格式化科学计数法,取一位整数
  decimalformat df = new decimalformat("0");
  returnvalue = df.format(doublevalue);
  break;
  case string: //字符串
  returnvalue = cell.getstringcellvalue();
  break;
  case boolean: //布尔
  boolean booleanvalue = cell.getbooleancellvalue();
  returnvalue = booleanvalue.tostring();
  break;
  case blank: // 空值
  break;
  case formula: // 公式
  returnvalue = cell.getcellformula();
  break;
  case error: // 故障
  break;
  default:
  break;
 }
 return returnvalue;
 }
 
 /**
 * 提取每一行中需要的数据,构造成为一个结果数据对象
 *
 * 当该行中有单元格的数据为空或不合法时,忽略该行的数据
 *
 * @param row 行数据
 * @return 解析后的行数据对象,行数据错误时返回null
 */
 private static exceldatavo convertrowtodata(row row) {
 exceldatavo resultdata = new exceldatavo();
 
 cell cell;
 int cellnum = 0;
 // 获取姓名
 cell = row.getcell(cellnum++);
 string name = convertcellvaluetostring(cell);
 resultdata.setname(name);
 // 获取年龄
 cell = row.getcell(cellnum++);
 string agestr = convertcellvaluetostring(cell);
 if (null == agestr || "".equals(agestr)) {
  // 年龄为空
  resultdata.setage(null);
 } else {
  resultdata.setage(integer.parseint(agestr));
 }
 // 获取居住地
 cell = row.getcell(cellnum++);
 string location = convertcellvaluetostring(cell);
 resultdata.setlocation(location);
 // 获取职业
 cell = row.getcell(cellnum++);
 string job = convertcellvaluetostring(cell);
 resultdata.setjob(job);
 
 return resultdata;
 }
}
2.2.3 应用场景补充
一般我们会有这样的应用场景,即:在前台页面的文件上传入口上传本地的excel文件到后台,后台收到excel文件后进行解析并做对应的业务操作;
这里假设前台已经有了上传文件的入口,再简单贴一下后台的解析代码;
后台接收前台数据的controller层代码示例:
@postmapping("/uploadexcel")
public responseentity<?> uploadimage(multipartfile file) {
 
 // 检查前台数据合法性
 if (null == file || file.isempty()) {
  logger.warning("上传的excel商品数据文件为空!上传时间:" + new date());
  return new responseentity<>(httpstatus.bad_request);
 }
 
 try {
  // 解析excel
  list<exceldatavo> parsedresult = excelreader.readexcel(file);
  // todo 进行业务操作
 
  return new responseentity<>(httpstatus.ok);
 } catch (exception e) {
  logger.warning("上传的excel商品数据文件为空!上传时间:" + new date());
  return new responseentity<>(httpstatus.bad_request);
 }
 
}
excelreader.java中的 readexcel() 方法需要做一定的修改,代码如下:
/**
 * 读取excel文件内容
 * @param file 上传的excel文件
 * @return 读取结果列表,读取失败时返回null
 */
public static list<exceldatavo> readexcel(multipartfile file) {
 
 workbook workbook = null;
 
 try {
  // 获取excel后缀名
  string filename = file.getoriginalfilename();
  if (filename == null || filename.isempty() || filename.lastindexof(".") < 0) {
   logger.warning("解析excel失败,因为获取到的excel文件名非法!");
   return null;
  }
  string filetype = filename.substring(filename.lastindexof(".") + 1, filename.length());
 
  // 获取excel工作簿
  workbook = getworkbook(file.getinputstream(), filetype);
 
  // 读取excel中的数据
  list<exceldatavo> resultdatalist = parseexcel(workbook);
 
  return resultdatalist;
 } catch (exception e) {
  logger.warning("解析excel失败,文件名:" + file.getoriginalfilename() + " 错误信息:" + e.getmessage());
  return null;
 } finally {
  try {
   if (null != workbook) {
  workbook.close();
   }
  } catch (exception e) {
   logger.warning("关闭数据流出错!错误信息:" + e.getmessage());
   return null;
  }
 }
}
2.3 写入数据
写入数据时主要调用excelwriter.java来创建excel工作簿对象并写入数据,这里以写入系统文件数据为例演示写入的过程:
2.3.1 主程序入口类代码
/**
 * author: dreamer-1
 * date: 2019-03-01
 * time: 10:13
 * description: 示例程序入口类
 */
public class maintest {
 
 private static logger logger = logger.getlogger(maintest.class.getname());
 
 public static void main(string[] args) {
  // 创建需要写入的数据列表
  list<exceldatavo> datavolist = new arraylist<>(2);
  exceldatavo datavo = new exceldatavo();
  datavo.setname("小明");
  datavo.setage(18);
  datavo.setlocation("广州");
  datavo.setjob("大学生");
  exceldatavo datavo2 = new exceldatavo();
  datavo2.setname("小花");
  datavo2.setage(19);
  datavo2.setlocation("深圳");
  datavo2.setjob("大学生");
  datavolist.add(datavo);
  datavolist.add(datavo2);
 
  // 写入数据到工作簿对象内
  workbook workbook = excelwriter.exportdata(datavolist);
 
  // 以文件的形式输出工作簿对象
  fileoutputstream fileout = null;
  try {
   string exportfilepath = "/users/dreamer-1/desktop/myblog/java解析excel/writeexample.xlsx";
   file exportfile = new file(exportfilepath);
   if (!exportfile.exists()) {
  exportfile.createnewfile();
   }
 
   fileout = new fileoutputstream(exportfilepath);
   workbook.write(fileout);
   fileout.flush();
  } catch (exception e) {
   logger.warning("输出excel时发生错误,错误原因:" + e.getmessage());
  } finally {
   try {
  if (null != fileout) {
   fileout.close();
  }
  if (null != workbook) {
   workbook.close();
  }
   } catch (ioexception e) {
  logger.warning("关闭输出流时发生错误,错误原因:" + e.getmessage());
   }
  }
 
 }
 
}
2.3.2 写入excel类的代码
excelwriter.java类中,你可以根据实际需要替换 cell_heads 列头的信息,然后重写 convertdatatorow 方法,转换你自己的行数据;
/**
 * author: dreamer-1
 * date: 2019-03-01
 * time: 11:09
 * description: 生成excel并写入数据
 */
public class excelwriter {
 
 private static list<string> cell_heads; //列头
 
 static{
  // 类装载时就载入指定好的列头信息,如有需要,可以考虑做成动态生成的列头
  cell_heads = new arraylist<>();
  cell_heads.add("姓名");
  cell_heads.add("年龄");
  cell_heads.add("居住城市");
  cell_heads.add("职业");
 }
 
 /**
  * 生成excel并写入数据信息
  * @param datalist 数据列表
  * @return 写入数据后的工作簿对象
  */
 public static workbook exportdata(list<exceldatavo> datalist){
  // 生成xlsx的excel
  workbook workbook = new sxssfworkbook();
 
  // 如需生成xls的excel,请使用下面的工作簿对象,注意后续输出时文件后缀名也需更改为xls
  //workbook workbook = new hssfworkbook();
 
  // 生成sheet表,写入第一行的列头
  sheet sheet = builddatasheet(workbook);
  //构建每行的数据内容
  int rownum = 1;
  for (iterator<exceldatavo> it = datalist.iterator(); it.hasnext(); ) {
   exceldatavo data = it.next();
   if (data == null) {
  continue;
   }
   //输出行数据
   row row = sheet.createrow(rownum++);
   convertdatatorow(data, row);
  }
  return workbook;
 }
 
 /**
  * 生成sheet表,并写入第一行数据(列头)
  * @param workbook 工作簿对象
  * @return 已经写入列头的sheet
  */
 private static sheet builddatasheet(workbook workbook) {
  sheet sheet = workbook.createsheet();
  // 设置列头宽度
  for (int i=0; i<cell_heads.size(); i++) {
   sheet.setcolumnwidth(i, 4000);
  }
  // 设置默认行高
  sheet.setdefaultrowheight((short) 400);
  // 构建头单元格样式
  cellstyle cellstyle = buildheadcellstyle(sheet.getworkbook());
  // 写入第一行各列的数据
  row head = sheet.createrow(0);
  for (int i = 0; i < cell_heads.size(); i++) {
   cell cell = head.createcell(i);
   cell.setcellvalue(cell_heads.get(i));
   cell.setcellstyle(cellstyle);
  }
  return sheet;
 }
 
 /**
  * 设置第一行列头的样式
  * @param workbook 工作簿对象
  * @return 单元格样式对象
  */
 private static cellstyle buildheadcellstyle(workbook workbook) {
  cellstyle style = workbook.createcellstyle();
  //对齐方式设置
  style.setalignment(horizontalalignment.center);
  //边框颜色和宽度设置
  style.setborderbottom(borderstyle.thin);
  style.setbottombordercolor(indexedcolors.black.getindex()); // 下边框
  style.setborderleft(borderstyle.thin);
  style.setleftbordercolor(indexedcolors.black.getindex()); // 左边框
  style.setborderright(borderstyle.thin);
  style.setrightbordercolor(indexedcolors.black.getindex()); // 右边框
  style.setbordertop(borderstyle.thin);
  style.settopbordercolor(indexedcolors.black.getindex()); // 上边框
  //设置背景颜色
  style.setfillforegroundcolor(indexedcolors.grey_25_percent.getindex());
  style.setfillpattern(fillpatterntype.solid_foreground);
  //粗体字设置
  font font = workbook.createfont();
  font.setbold(true);
  style.setfont(font);
  return style;
 }
 
 /**
  * 将数据转换成行
  * @param data 源数据
  * @param row 行对象
  * @return
  */
 private static void convertdatatorow(exceldatavo data, row row){
  int cellnum = 0;
  cell cell;
  // 姓名
  cell = row.createcell(cellnum++);
  cell.setcellvalue(null == data.getname() ? "" : data.getname());
  // 年龄
  cell = row.createcell(cellnum++);
  if (null != data.getage()) {
   cell.setcellvalue(data.getage());
  } else {
   cell.setcellvalue("");
  }
  // 所在城市
  cell = row.createcell(cellnum++);
  cell.setcellvalue(null == data.getlocation() ? "" : data.getlocation());
  // 职业
  cell = row.createcell(cellnum++);
  cell.setcellvalue(null == data.getjob() ? "" : data.getjob());
 }
}
示例程序运行后将会在指定的系统路径下生成 writeexample.xlsx文件,其内容如下:
DSC0001.png

2.3.3 应用场景补充
一般写入excel时会有这样的场景:前台页面上有一个导出按钮,点击后将后台某张表里的数据以excel的形式导出,导出的excel文件通过浏览器下载到用户系统中;
这里默认前台页面已经有相应的按钮功能,给出对应的controller层代码供参考:
@getmapping("/exportexcel")
public void exportexcel(httpservletrequest request, httpservletresponse response) {
 workbook workbook = null;
 outputstream out = null;
 try {
  // todo 根据业务需求获取需要写入excel的数据列表 datalist
 
  // 生成excel工作簿对象并写入数据
  workbook = excelwriter.exportdata(datalist);
 
  // 写入excel文件到前端
  if(null != workbook){
   string excelname = "示例excel导出";
   string filename = excelname + dateutil.format(new date(), dateutil.short_date) + ".xlsx";
   filename = new string(filename.getbytes("utf-8"),"iso8859-1");
   response.setheader("content-disposition", "attachment;filename=" + filename);
   response.setcontenttype("application/x-download");
   response.setcharacterencoding("utf-8");
   response.addheader("pargam", "no-cache");
   response.addheader("cache-control", "no-cache");
   response.flushbuffer();
   out = response.getoutputstream();
   workbook.write(out);
   out.flush();
  }
 } catch (exception e) {
  logger.warning("写入excel过程出错!错误原因:" + e.getmessage());
 } finally {
  try {
   if (null != workbook) {
  workbook.close();
   }
   if (null != out) {
  out.close();
   }
  } catch (ioexception e) {
   logger.warning("关闭workbook或outputstream出错!");
  }
 }
}
// 前台页面发送请求到后台controller时的js代码可参考:
 
var url = "/exportexcel";
window.location=url;
3. 源码下载
点我哦.
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对CodeAE代码之家的支持。
原文链接:https://www.cnblogs.com/Dreamer-1/p/10469430.html

关注下面的标签,发现更多相似文章