导包
第一步从导包开始,关于这个Excel表,已经有了一套现成的依赖包:
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.6</version>
</dependency>
这个也是我在网上找的依赖,各种帖子中的依赖版本不同,反正这个3.6是我目前看到的最高版本。至于有没有更新的不确定,反正这个可以使用。然后好多帖子引用了几个依赖,但是反正我实测,导入导出这一个依赖就能搞定。
导入Excel文件工具类
这个分两个,一个是导入工具,一个是导出工具
大概说一下工作原理:导入就是把Excel文件解析,一个单元格一个单元格的转化成想要的数据。有的转成数组,有的转成map,我反正直接转成想要的对象了。
我也看了一些技术贴的做法,有的简单有的麻烦,反正大体过程是一样的。接下来上代码(因为涉及到公司数据格式,所以对象用User来代替):
/**
* 检验文件是否有效
*
* @param file
* @throws Exception
*/
public static void checkFile(MultipartFile file) throws Exception {
// 判断文件是否存在
if (null == file) {
throw new FileNotFoundException("文件不存在!");
}
// 获得文件名
String fileName = file.getOriginalFilename();
// 判断文件是否是excel文件
if (!fileName.endsWith("xls") && !fileName.endsWith("xlsx")) {
throw new IOException(fileName + "不是excel文件");
}
}
上面这个方法单纯的为了判断一个文件是否是Excel文件。毕竟文件格式不对整个解析都不能继续。
/**
* 获取workbook
*
* @param file
* @return
*/
public static Workbook getWorkBook(MultipartFile file) {
// 获得文件名
String fileName = file.getOriginalFilename();
// 创建Workbook工作薄对象,表示整个excel
Workbook workbook = null;
try {
// 获取excel文件的io流
InputStream is = file.getInputStream();
// 根据文件后缀名不同(xls和xlsx)获得不同的Workbook实现类对象
if (fileName.endsWith("xls")) {
// 2003
workbook = new HSSFWorkbook(is);
} else if (fileName.endsWith("xlsx")) {
// 2007
workbook = new XSSFWorkbook(is);
}
} catch (IOException e) {
}
return workbook;
}
这个方法是为了获取文件的workbook实现类对象。因为Excel文件分为xls和xlsx两种格式,所以要做个判断。
其实这两个方法写在一起,或者说这个三个方法写在一起都可以,但是我还是分开写了,这样每个类干什么的就会很清楚的知道了。
/**
* 获取解析后的文件内容
*
* @return
*/
public static List<User> readExcel(MultipartFile file) {
List<User> list = new ArrayList<User>();
try {
checkFile(file);
// 获得Workbook工作薄对象
Workbook workbook = getWorkBook(file);
int sheetSize = workbook.getNumberOfSheets();
for (int i = 0; i < sheetSize; i++) {// 遍历sheet页
// 获取第一个张表
Sheet sheet = workbook.getSheetAt(0);
// 获取每行中的字段
for (int j = 0; j <= sheet.getLastRowNum(); j++) {
Row row = sheet.getRow(j); // 获取行
if (row == null) {//略过空行
continue;
}else{
// 获取单元格中的值并存到对象中
User user = new User();
user.setFullName(row.getCell(0).getStringCellValue());
user.setPassword(row.getCell(1).getStringCellValue());
user.setUsername(row.getCell(2).getStringCellValue());
list.add(user);
}
}
}
} catch (Exception e) {
throw new Exception("Excel导入失败!");
}
return list;
}
最后这个方法是把workbook对象转化成自己想要的。注解写的很清楚,首先遍历页,然后遍历行,每一行在拆分,每行第一个单元格下标是0,依次顺序往后。这个顺序注意,我反正是表格和实体对应着一个个取出来set进去的。尤其是如果一行十几个甚至很多的数据,千万别写反了就行。
demo中用的user来做例子,所以只有三列,但是实际上肯定比这个要麻烦的。最后把一行一行解析的对象放入list中返回。
因为这个get的过程肯定要针对具体情况来写,所以我直接一步到位set进对象里,我看很多教程都是放到map中,然后在map集合遍历取出放到对象中,具体要怎么做都可以。
反正这个方法得到的结果集就是我们解析exc文件获取到的想要的结果集。
数据格式问题:
但是这个是很空洞的情况,真正解析起来各种数据是不同的,最简单的比方,一个时间日期格式也用String类型接收,就会从日期变成小数,解析都不好解析的那种。
同理,如果是数字类型的格子,也没必要先换成string接收再转回数字。
我们可以用set方法先设置好某个格子是什么数据类型的。
例如:row.getCell(1).setCellType(Cell.CELL_TYPE_NUMERIC);
上面的代码就是设置第二个格子(因为从0开始,所以1是第二个格子)是数值类型的。
poi类有很多不同常量的定义:
这个设置和获取要配对,比如设置布尔类型,然后用string类型获取就会报错:
Cannot get a text value from a numeric cell
这个错就是不能从一个数值类型获取string。
判空问题
这个我感觉应该也是很常见的一个需求,我们要判断excel文件中某些必填字段是否为空,如果是则直接返回前端而不浪费时间。
其实我提出来这个是因为我在这里遇到了一个小小的坑,我以为取出来如果是null或者如果是“”则为空。
但是实际上如果格子里面没有内容,在取的过程中就会报错!因为你把一个blank(空)格式的格子用别的类型来获取了!
所以这里的非空判断要做一点小小的修改:
row.getCell(k) == null
直接用getCell来判断,没有下一步的获取内容。
导出Excel文件工具类
这个导入导出用的都是这一个依赖,所以就不多说了。直接说操作:
导入的原理就是把对象集合以Excel表格的形式导出。
大体的操作流程也差不多,就是设置文件格式,设置表头,然后往里循环添加数据。下面是完整的代码例子(为了防止公司数据泄露所以用user举例):
public static HSSFWorkbook getHSSFWorkbook(List<User> list){
// 第一步,创建一个HSSFWorkbook,对应一个Excel文件
HSSFWorkbook wb = new HSSFWorkbook();
// 第二步,在workbook中添加一个sheet,对应Excel文件中的sheet
HSSFSheet sheet = wb.createSheet();
// 第三步,在sheet中添加表头第0行,注意老版本poi对Excel的行数列数有限制
HSSFRow row = sheet.createRow(0);
// 第四步,创建单元格,并设置值表头 设置表头居中
HSSFCellStyle style = wb.createCellStyle();
style.setAlignment(HSSFCellStyle.ALIGN_CENTER); // 创建一个居中格式
HSSFCell cell1 = row.createCell(0);
cell1.setCellValue("日期");
cell1.setCellStyle(style);
HSSFCell cell2 = row.createCell(1);
cell2.setCellValue("昵称");
cell2.setCellStyle(style);
HSSFCell cell3 = row.createCell(2);
cell3.setCellValue("名称");
cell3.setCellStyle(style);
HSSFCell cell4 = row.createCell(3);
cell4.setCellValue("年龄");
cell4.setCellStyle(style);
HSSFCell cell5 = row.createCell(4);
cell5.setCellValue("体重");
cell5.setCellStyle(style);
HSSFCell cell6 = row.createCell(5);
cell6.setCellValue("爱好");
cell6.setCellStyle(style);
HSSFCell cell7 = row.createCell(6);
cell7.setCellValue("工作");
cell7.setCellStyle(style);
HSSFCell cell8 = row.createCell(7);
cell8.setCellValue("家庭住址");
cell8.setCellStyle(style);
HSSFCell cell9 = row.createCell(8);
cell9.setCellValue("婚恋状态");
cell9.setCellStyle(style);
HSSFCell cell10 = row.createCell(9);
cell10.setCellValue("工作单位");
cell10.setCellStyle(style);
HSSFCell cell11 = row.createCell(10);
cell11.setCellValue("性别");
cell11.setCellStyle(style);
HSSFCell cell12 = row.createCell(11);
cell12.setCellValue("是否是新用户");
cell12.setCellStyle(style);
HSSFCell cell13 = row.createCell(12);
cell13.setCellValue("密码");
cell13.setCellStyle(style);
//创建内容
for(int i=0;i<list.size();i++){
//第一行是表头,第二行开始插数据
row = sheet.createRow(i + 1);
row.createCell(0).setCellValue(new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
row.createCell(1).setCellValue(list.get(i).getNick());
row.createCell(2).setCellValue(list.get(i).getName());
//插入的数据是数字
Cell cella = row.createCell(3);
cella.setCellType(Cell.CELL_TYPE_NUMERIC);
cella.setCellValue(list.get(i).getAge());
Cell cellb = row.createCell(4);
cellb.setCellType(Cell.CELL_TYPE_NUMERIC);
cellb.setCellValue(list.get(i).getWeight().doubleValue());
Cell cellc = row.createCell(5);
//别问我为啥爱好是double,我就是测试数据类型
cellc.setCellType(Cell.CELL_TYPE_NUMERIC);
cellc.setCellValue(list.get(i).getLike().doubleValue());
row.createCell(6).setCellValue(list.get(i).getWork());
row.createCell(7).setCellValue(list.get(i).getAddress());
row.createCell(8).setCellValue(list.get(i).getLoveStatus());
row.createCell(9).setCellValue(list.get(i).getWorkAddress());
row.createCell(10).setCellValue(list.get(i).getSex());
Cell celld = row.createCell(11);
celld.setCellType(Cell.CELL_TYPE_NUMERIC);
celld.setCellValue(list.get(i).getIsNew()==null?0: list.get(i).getIsNew().intValue());
row.createCell(12).setCellValue(list.get(i).getPassword());
}
return wb;
}
如上,一个数据导出到Excel文件完成。不过这个还只是数据格式,而不是一个真真实实可打开的xls文件。
因为我们的项目是前后端分离的,也不要求我在服务器生成一个文件啥的,主要还是把数据发给前端,所以还有个发送给前端的过程(最简化操作,一点业务逻辑没写,根据实际情况添加):
public void export(HttpServletResponse response) {
//excel文件名
String fileName = "测试一号.xls";
//创建HSSFWorkbook,别问我list哪来的,我哪知道你数据哪来的
HSSFWorkbook wb = getHSSFWorkbook(list);
//响应到客户端
try {
this.setResponseHeader(response, fileName);
OutputStream os = response.getOutputStream();
wb.write(os);
os.flush();
os.close();
} catch (Exception e) {
e.printStackTrace();
}
}
网友评论