在项目中经常出现的需求就是,需要将表格形式的数据导出或者将表格形式的数据导入,当然我们可以使用简单的文本格式,使用csv作为后缀,客户也可以使用excel打开,但是那样会出现各种奇怪的问题,比较常见的就是乱码。
在java里面其实已经有了很好的操作库,可以很容易的进行excel文件的操作,那就是Apache POI,本文将延时如何使用poi中的接口,对表格形式数据进行导出和导入。
注:poi还有操作其他文档的库,就不做讨论了。
添加依赖
我们使用maven进行项目的构建管理,在依赖中添加下面的配置即可
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.16</version>
</dependency>
由于excel文件一般有xls后缀和xlsx后缀,这两种文件有较大差异,操作的api是单独的,上面的依赖是操作xlsx所必须的依赖,maven会自动寻找它所以来的poi的组件,如果仅仅是需要操作xls文件,那么可以直接配置
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.16</version>
</dependency>
本文的项目为了演示两种api的操作,使用的poi-ooxml的组件。
关于项目代码已经上传到了码云,https://git.oschina.net/songxinqiang/BlogExampleCode,可以自行查看
准备
为了演示的需要,我准备了一个模型类,用于表示表格的“一行”,就是一个简单的POJO
public class User implements Serializable {
private static final long serialVersionUID = 5643553301653588278L;
private int id;
private String username;
public User() {}
public User(int id, String username) {
this.id = id;
this.username = username;
}
@Override
public String toString() {
return "{id:" + id + ",username:" + username + "}";
}
//set、get
}
为了实现导出,我们还需要准备一些数据
List<User> userList = new ArrayList<>();
userList.add(new User(1, "test1"));
userList.add(new User(2, "test2"));
userList.add(new User(3, "test3"));
userList.add(new User(4, "test4"));
userList.add(new User(5, "test5"));
userList.add(new User(6, "test6"));
userList.add(new User(7, "test7"));
userList.add(new User(8, "test8"));
导出表格到文件
将前面的list数据保存到一个文件中,我们需要建立一个excel文件的描述对象,在这个对象中添加数据即可。
如开始时所述,xls和xlsx的文件时不同的,下面分别给出两个方法,首先是操作xls文件的
/**
* 将用户列表转换存入xls文件
*
* @param userList
* 用户信息列表
* @return 保存的临时文件的信息对象,用于后续操作
*/
public File userListToFile(List<User> userList) {
HSSFWorkbook workbook = new HSSFWorkbook();
HSSFSheet spreadsheet = workbook.createSheet("用户列表");
// 用于第一行的样式,当做标题
HSSFFont font = workbook.createFont();
font.setFontHeightInPoints((short) 14);
font.setItalic(true);
font.setBold(true);
HSSFCellStyle style = workbook.createCellStyle();
style.setFont(font);
HSSFRow row;
Cell cell;
int rowid = 0;
int cellid = 0;
row = spreadsheet.createRow(rowid++);
row.setRowStyle(style);
row.setHeightInPoints(27);
cell = row.createCell(cellid);
cell.setCellValue("编号");
spreadsheet.setColumnWidth(cellid, 18 * 256);
cellid++;
cell = row.createCell(cellid);
cell.setCellValue("用户名");
spreadsheet.setColumnWidth(cellid, 18 * 256);
cellid++;
for (User user : userList) {
try {
cellid = 0;
row = spreadsheet.createRow(rowid++);
cell = row.createCell(cellid++);
cell.setCellValue(user.getId());
cell = row.createCell(cellid++);
cell.setCellValue(user.getUsername());
} catch (Exception e) {
logger.error("parse list error," + e.getMessage());
}
}
// 文件只是临时文件,在方法返回之后,对文件进行后续的移动位置等操作
File file = new File("/tmp/userlist.xls");
try {
FileOutputStream fos = new FileOutputStream(file);
workbook.write(fos);
} catch (Exception e) {
logger.error("write file failed," + e.getMessage());
} finally {
try {
workbook.close();
} catch (IOException e1) {
logger.error("close workbook error," + e1.getMessage());
}
}
return file;
}
然后是xlsx文件的
/**
* 将用户列表转换存入xlsx文件
*
* @param userList
* 用户信息列表
* @return 保存的临时文件的信息对象,用于后续操作
*/
public File userListToFile(List<User> userList) {
XSSFWorkbook workbook = new XSSFWorkbook(XSSFWorkbookType.XLSX);
XSSFSheet spreadsheet = workbook.createSheet("用户列表");
// 用于第一行的样式,当做标题
XSSFFont font = workbook.createFont();
font.setFontHeightInPoints((short) 14);
font.setItalic(true);
font.setBold(true);
XSSFCellStyle style = workbook.createCellStyle();
style.setFont(font);
XSSFRow row;
Cell cell;
int rowid = 0;
int cellid = 0;
row = spreadsheet.createRow(rowid++);
row.setRowStyle(style);
row.setHeightInPoints(27);
cell = row.createCell(cellid);
cell.setCellValue("编号");
spreadsheet.setColumnWidth(cellid, 18 * 256);
cellid++;
cell = row.createCell(cellid);
cell.setCellValue("用户名");
spreadsheet.setColumnWidth(cellid, 18 * 256);
cellid++;
for (User user : userList) {
try {
cellid = 0;
row = spreadsheet.createRow(rowid++);
cell = row.createCell(cellid++);
cell.setCellValue(user.getId());
cell = row.createCell(cellid++);
cell.setCellValue(user.getUsername());
} catch (Exception e) {
logger.error("parse list error," + e.getMessage());
}
}
// 文件只是临时文件,在方法返回之后,对文件进行后续的移动位置等操作
File file = new File("/tmp/userlist.xlsx");
try {
FileOutputStream fos = new FileOutputStream(file);
workbook.write(fos);
} catch (Exception e) {
logger.error("write file failed," + e.getMessage());
} finally {
try {
workbook.close();
} catch (IOException e1) {
logger.error("close workbook error," + e1.getMessage());
}
}
return file;
}
可以看到,他们在操作上大体的方法是一样的,只有少许的差异,具体的代码的意义,还是需要查询javadoc文档的,通过这个方法我们就得到了一个保存有列表中数据的File对象,可以用于后续的处理,在这里我们直接用于文件中数据导入的数据源。
导入表格数据到程序
和导出一样,导入也需要分文件类型进行操作,下面首先是导入xls文件数据的
/**
* 读取文件中的值得到记录列表
*
* @param file
* 文件描述信息
* @return 记录对象列表
*/
public List<User> fileToList(File file) {
List<User> userList = new ArrayList<>();
HSSFWorkbook workbook = null;
try {
NPOIFSFileSystem poifs = new NPOIFSFileSystem(file);
workbook = new HSSFWorkbook(poifs);
} catch (Exception e) {
logger.error("read file error:" + e.getMessage());
return userList;
}
HSSFSheet spreadsheet = workbook.getSheetAt(0);
int rowCount = spreadsheet.getLastRowNum();
HSSFRow row;
// 第一行忽略
for (int rowId = 1; rowId <= rowCount; rowId++) {
row = spreadsheet.getRow(rowId);
User user = new User();
// 这里获取“数字”类型的值得到的是double
user.setId((int) row.getCell(0).getNumericCellValue());
user.setUsername(row.getCell(1).getStringCellValue());
userList.add(user);
}
try {
workbook.close();
} catch (IOException e) {}
return userList;
}
接下来,是xlsx文件的操作
/**
* 读取文件中的值得到记录列表
*
* @param file
* 文件描述信息
* @return 记录对象列表
*/
public List<User> fileToList(File file) {
List<User> userList = new ArrayList<>();
XSSFWorkbook workbook = null;
try {
workbook = new XSSFWorkbook(file);
} catch (Exception e) {
logger.error("read file error:" + e.getMessage());
return userList;
}
XSSFSheet spreadsheet = workbook.getSheetAt(0);
int rowCount = spreadsheet.getLastRowNum();
XSSFRow row;
// 第一行忽略
for (int rowId = 1; rowId <= rowCount; rowId++) {
row = spreadsheet.getRow(rowId);
User user = new User();
// 这里获取“数字”类型的值得到的是double
user.setId((int) row.getCell(0).getNumericCellValue());
user.setUsername(row.getCell(1).getStringCellValue());
userList.add(user);
}
try {
workbook.close();
} catch (IOException e) {}
return userList;
}
这里,我们只关心里面的数据,所以导入的时候并没有关心格式那些,代码旧比较简单,有时候可能会关心格式,甚至关心数据的类型那些,具体处理起来就会麻烦一些。
同时,我们同样会发现,其实两种文件的处理代码还是差不多的。
测试
测试的代码比较简单,就是准备数据,调用方法处理,然后观察结果
public static void main(String[] args) {
List<User> userList = new ArrayList<>();
userList.add(new User(1, "test1"));
userList.add(new User(2, "test2"));
userList.add(new User(3, "test3"));
userList.add(new User(4, "test4"));
userList.add(new User(5, "test5"));
userList.add(new User(6, "test6"));
userList.add(new User(7, "test7"));
userList.add(new User(8, "test8"));
PoiXlsxService poiXlsx = new PoiXlsxService();
File xlsx = poiXlsx.userListToFile(userList);
PoiXlsService poiXls = new PoiXlsService();
File xls = poiXls.userListToFile(userList);
userList = poiXlsx.fileToList(xlsx);
System.out.println(Arrays.toString(userList.toArray()));
userList = poiXls.fileToList(xls);
System.out.println(Arrays.toString(userList.toArray()));
}
小结
文中演示的只是基本简单的操作,基本的数据导出和导入,具体的操作还有很多奇妙的地方,还需要仔细发现。