1 前言
大家可能对阿里的easypoi比较熟悉,简简单单的一两行代码就能够进行导入和导出的操作。但是平时在很多地方,我们还是用到了Apache的poi。所以这里做一个简单的记录。
有关easypoi使用文章:CSDN
2 基本功能
结构:
HSSF - 提供读写Excel格式文档的功能
XSSF - 提供读写ExcelOOXML格式档案的功能
HWPF - 提供Word格式档案的功能
HSLF - 提供读写PowerPoint格式文档的功能
HDGF - 提供Visio格式档案的功能
注意:Apache的poi存在一个很严重的问题就是非常的耗内存,poi有一套SAX模式虽然可以在一定程度上可以解决一些内存溢出的问题,但依然会有一些缺陷。07版本的Excel解压缩和解压后的存储都是在内存中完成的,内存消耗很大。而easyExcel就不会出现内存溢出的问题。
想要搞清楚poi对Excel表格中从操作,我们需要先搞清楚其中的四个对象:
//创建一个工作簿(07版本的) XSSFWorkbook workbook = new XSSFWorkbook(); //创建一个工作表 XSSFSheet sheet = workbook.createSheet(); //创建一个行 XSSFRow row = sheet.createRow(0); //创建一个单元格 XSSFCell cell = row.createCell(0);
先创建一个工作簿(也就是一个Excel表格)
然后根据excel去创建一个工作表 (就是excel表格中的sheet1,,sheet2)
然后根据表格去创建一个行
最后根据行去创建列
这样我们就可以确定我们的一个坐标
注意:当我们大文件写入HSSF(03版)时
缺点:最多只能处理65536行,否则会抛出异常
优点:过程中写入缓存,不操作磁盘,最后一次性操作缓存,速度快
当我们大文件写入XSSF(07版)时
缺点:写数据时速度非常快,非常耗内存,也会发生内存溢出
优点:可以写入较大的数据
3 代码演示
相关准备
导入相关依赖:
<!--xls(03)-->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.1.2</version>
</dependency>
<!--xlsx(07)-->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>
<!--日期格式化工具-->
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.10.5</version>
</dependency>
数据库插入数据:
3.1 导出数据
我们演示将这些数据读出来放入excel表格中:
代码如下:
@Controller
@Slf4j
public class RestController {
@Resource
private UserMapper userMapper;
@PostMapping("/get")
public void getUser(HttpServletRequest request, HttpServletResponse response) throws IOException {
QueryWrapper queryWrapper = new QueryWrapper();
queryWrapper.like("name", "张");
List<User> users = userMapper.selectList(queryWrapper);
System.out.println("******"+users);
//定义表格名称并且指定编码格式
String encode = URLEncoder.encode("导出文件" + ".xlsx", "utf-8");
response.setHeader("Content-Disposition", "attachment;filename=" + encode);
OutputStream outputStream = response.getOutputStream();
List<List<String>> listStrings = new ArrayList<>();
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
users.forEach(user->{
List<String> list1 = new ArrayList<>();
list1.add(String.valueOf(user.getId()));
list1.add(user.getName());
list1.add(format.format(user.getCreateTime()));
list1.add(user.getUniqueId());
list1.add(user.getNotice());
listStrings.add(list1);
});
log.info("将要导出的数据:"+listStrings);
//创建一个工作簿
XSSFWorkbook workbook = new XSSFWorkbook();
//创建一个工作表
XSSFSheet sheet = workbook.createSheet();
//表头在第一行 将表头中的数据循环放入第一行的表头中
XSSFRow row = sheet.createRow(0);
String[] heads = headNames();
//填充表头数据
for (int i = 0; i < headNames().length; i++) {
XSSFCell cell = row.createCell(i);
cell.setCellValue(heads[i]);
}
//填充实际数据
if (CollectionUtils.isNotEmpty(listStrings)){
for (int i = 0; i < listStrings.size(); i++) {
//表头数据上面已经填充过了,然后从第二行开始填充
XSSFRow row1 = sheet.createRow(i + 1);
XSSFCell cell = row1.createCell(0);
cell.setCellValue(i+1);
//将user对象中的值填充到表格中
List<String> list = listStrings.get(i);
for (int j = 0; j < list.size(); j++) {
XSSFCell cell1 = row1.createCell(j + 1);
cell1.setCellValue(list.get(j));
}
}
}
//将产生的workbook对象放入输出流中
workbook.write(outputStream);
//刷新流
outputStream.flush();
//关闭流
outputStream.close();
}
private String[] headNames(){
return new String[]{
"序号",
"id",
"姓名",
"创建时间",
"分布式id",
"注意事项"
};
}
}
然后我们用postman去进行测试:
点击这个Send and Download按钮,这时会弹出下载框,
注意:只要中文名称是中文的,文件名称就会乱码(不用惊慌,postman测试导出就是这样的),在浏览器上运行就没有这个问题
以下是我们的导出的文件
3.2 导入数据
代码如下:
@PostMapping("/import")
public void importExcel( @RequestBody MultipartFile file ) throws IOException {
log.info("excel文件路径名称>>>>>>"+file);
InputStream inputStream = null;
try {
inputStream = file.getInputStream();
} catch (IOException e) {
log.info("读取文件出错");
}
/* ExcelReader excelReader = new ExcelReader(inputStream,0);
List<Map<String, Object>> mapList = excelReader.readAll();
log.info("引用hutool工具类读取数据:"+mapList);*/
//读取工作簿中第一个表中的内容
XSSFWorkbook workbook = new XSSFWorkbook(inputStream);
XSSFSheet sheetAt = workbook.getSheetAt(0);
//读取表头
XSSFRow rowTitle = sheetAt.getRow(0);
if (rowTitle != null ) {
//该行一共有多少列
int number = rowTitle.getPhysicalNumberOfCells();
for (int cellNum = 0; cellNum < number; cellNum++) {
XSSFCell cell = rowTitle.getCell(cellNum);
if (cell != null ) {
//改列的数据类型
CellType cellType = cell.getCellType();
String stringCellValue = cell.getStringCellValue();
log.info("数值为>>>>>:"+stringCellValue);
}
}
}
int rowsNum = sheetAt.getPhysicalNumberOfRows();
for (int rowNum = 1; rowNum < rowsNum; rowNum++) {
XSSFRow row = sheetAt.getRow(rowNum);
if (row != null ) {
int number = rowTitle.getPhysicalNumberOfCells();
for (int cellNum = 0; cellNum < number; cellNum++) {
XSSFCell cell = row.getCell(cellNum);
//匹配数据类型
if (cell != null ) {
CellType cellType = cell.getCellType();
String cellValue = "";
switch (cellType) {
//字符串类型
case STRING:
cellValue = cell.getStringCellValue();
break;
//布尔类型
case BOOLEAN:
cellValue = String.valueOf(cell.getBooleanCellValue());
break;
//空
case BLANK:
break;
//数字(日期或者普通数字)
case NUMERIC:
if (HSSFDateUtil.isCellDateFormatted(cell)){
cellValue = new DateTime(cell.getDateCellValue()).toString("yyyy-MM-dd");
}else {
cell.setCellType(CellType.STRING);
cellValue = cell.toString();
}
break;
case ERROR:
break;
}
log.info("数值为>>>>>:"+cellValue);
}
}
}
}
inputStream.close();
}
但我们可以发现这样导入的数据非常麻烦,所以我们可以用hutol的工具类:
/* ExcelReader excelReader = new ExcelReader(inputStream,0);
List<Map<String, Object>> mapList = excelReader.readAll();
log.info("引用hutool工具类读取数据:"+mapList);*/
可以很轻松的拿到我们想要的数据。