Java教程:使用POI读取excel文档(根据BV1bJ411G7Aw整理)
最近公司需要我做一个导出Excel表格的功能,为此来学习一下POI,在这里记录一下学习笔记。B站直接搜BV1bJ411G7Aw就能找到视频。
一、简介
poi如何操作表格
二、入门案例
2.1 从Excel文件读取数据
2.1.1 步骤文字描述
1.创建工作簿
2.获取工作表
3.遍历工作表获得行对象
4.遍历行对象获取单元格对象
5.获得单元格中的值
2.2.2 代码
先在pom中导入依赖
<!--poi-->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.14</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.14</version>
</dependency>
在桌面(你喜欢的地方)创建一个xlsx,输入一些简单数据
按照步骤书写代码
// 1.获取工作簿
XSSFWorkbook workbook = new XSSFWorkbook("C:\\Users\\ghost\\Desktop\\hello.xlsx");
// 2.获取工作表
// xlsx第一个工作簿(Sheet1),下标从0开始,0就是第一个
XSSFSheet sheet = workbook.getSheetAt(0);
/*使用加强for循环的方式*/
// 3.获取行
for (Row row : sheet) {
// 4.获取单元格
for (Cell cell: row) {
// 获取单元格中的内容
String value = cell.getStringCellValue();
System.out.println(value);
}
}
// 释放资源
workbook.close();
运行代码
我们会发现能够正常读到,但是因为我们也打开着这个文档,所以显示被占用
关掉再运行就一切正常了
我们再试试不使用加强for循环的方式
// 1.获取工作簿
XSSFWorkbook workbook = new XSSFWorkbook("C:\\Users\\ghost\\Desktop\\hello.xlsx");
// 2.获取工作表
// xlsx第一个工作簿(Sheet1),下标从0开始,0就是第一个
XSSFSheet sheet = workbook.getSheetAt(0);
/*使用普通循环的方式*/
// 开始索引(0) 结束索引
int lastRowNum = sheet.getLastRowNum(); //行的结束索引
for (int i = 0; i <= lastRowNum; i++) {
// 拿到行
XSSFRow row = sheet.getRow(i);
if (row != null){
short cellNum = row.getLastCellNum(); //单元格的结束索引
for (int j = 0; j <= cellNum; j ++){
XSSFCell cell = row.getCell(j);
// 单元格不为空就去拿他的值
if (cell != null){
String stringCellValue = cell.getStringCellValue();
System.out.println(stringCellValue);
}
}
}
}
// 释放资源
workbook.close();
能够正常运行
2.2 向Excel文件写入数据
2.2.1 步骤文字描述
1.创建一个Excel文件
2.创建工作表
3.创建行
4.创建单元格赋值
5.通过输出流将对象下载到磁盘
2.2.2 代码
// 1.创建工作簿
XSSFWorkbook workbook = new XSSFWorkbook();
// 2.创建工作表
XSSFSheet sheet = workbook.createSheet("工作表一");
// 3.创建行
XSSFRow row1 = sheet.createRow(0); //第一行
// 创建单元格
row1.createCell(0).setCellValue("众"); //第一行第一格
row1.createCell(1).setCellValue("子"); //第一行第二格
row1.createCell(2).setCellValue("之"); //第一行第三格
row1.createCell(3).setCellValue("爹"); //第一行第四格
XSSFRow row2 = sheet.createRow(1); //第二行
// 创建单元格
row2.createCell(0).setCellValue("众"); //第二行第一格
row2.createCell(1).setCellValue("子"); //第二行第二格
row2.createCell(2).setCellValue("之"); //第二行第三格
row2.createCell(3).setCellValue("爹"); //第二行第四格
// 输出流
FileOutputStream out = new FileOutputStream("C:\\Users\\ghost\\Desktop\\hello1.xlsx");
// 写入
workbook.write(out);
// 刷新流
out.flush();
// 释放资源
out.close();
workbook.close();
// 给个成功提示
System.out.println("写入成功!");
运行代码
回到桌面查看可以看到多出来一个hello1
三、配合数据库使用
3.1 读取Excel文件数据
首先在随便一个地方(我这里在桌面)创建一个product.xlsx
然后写入一些数据
创建数据库
创建实体类(getset方法、有参无参构造、toString)
这里使用控制台来代替前端页面,主要是展示功能代码
public class PioShow {
public static void main(String[] args) throws IOException {
// 通过键盘录入Scanner
Scanner scanner = new Scanner(System.in);
System.out.println("请输入你要选择的功能:1、导入;2、导出");
int num = scanner.nextInt();
if (num == 1){
// 1.导入
// 1.1读取excel表中的数据
System.out.println("请输入您要读取的文件位置(不包含空格):");
String path = scanner.next();
List<Pio> pioList = read(path);
System.out.println(pioList);
// 1.2将数据接入到数据库中
}else if (num == 2){
// 2.导出
// 2.1读取数据库中的数据
// 2.2将数据写入到excel表格中
}else {
System.out.println("输入有误,请重新启动!");
}
}
public static List<Pio> read(String path) throws IOException {
// 用来存储下面的存储单元格的list
List<Pio> pioList = new ArrayList<>();
// 1.获取工作簿
XSSFWorkbook xssfWorkbook = new XSSFWorkbook(path);
// 2.获取工作表
XSSFSheet sheet = xssfWorkbook.getSheetAt(0); //0代表第一个工作表
int lastRowNum = sheet.getLastRowNum();
// i=1,第一行的商品编号、商品名称、商品价格(单位:元/斤)、商品库存(单位:吨)不拿,不然报错java.lang.NumberFormatException: For input string: "商品编号"
for (int i = 1; i <= lastRowNum; i++) {
// 拿到行
XSSFRow row = sheet.getRow(i);
if (row != null){
// 用户存储每次获取的单元格内容
List<String> list = new ArrayList<>();
// 拿到单元格
for (Cell cell : row){
if (cell != null){
// 手动设置为字符串类型,避免乱码
cell.setCellType(Cell.CELL_TYPE_STRING);
// 获取单元格中的内容
String value = cell.getStringCellValue();
// 防止某些单元格为空
if (value != null && !"".equals(value)){
// 获取到的单元格内容放到list里面
list.add(value);
}
}
}
if (list.size() > 0){
// 0-3对应:主键,商品名称,商品价格,商品库存。除了商品名称本身就是String类型,其他全部要转换为对应类型
Pio pio = new Pio(Integer.parseInt(list.get(0)), list.get(1), Double.parseDouble(list.get(2)), Integer.parseInt(list.get(3)));
// 封装到List里
pioList.add(pio);
}
}
}
return pioList;
}
}
启动,然后输入文件路径,可以看到能够读取到里面的数据
最后添加保存到数据库的代码(具体见视频)
3.2 写出到Excel文件
这里以springBoot为例(之前自己写的一个小说网站),需要jdbc的例子的到视频看
注释掉的部分是固定写到桌面;这里用的是jsp,等待jsp返回地址,不为空就执行导出到excel
// 从数据库读取数据写到Excel中
@RequestMapping("/writePio")
public String writePio(String path) throws IOException {
//FileSystemView fsv = FileSystemView.getFileSystemView(); //注意了,这里重要的一句
//System.out.println(fsv.getHomeDirectory()); //得到桌面路径
//path = fsv.getHomeDirectory() + "\\aaa.xlsx";
// 2.导出
// 2.1读取数据库中的数据
List<Pio> pioList = novelService.selectPioAll();
// 2.2将数据写入到excel表格中
if(path != null) {
write(pioList, path);
System.out.println("写入成功!");
}
return "writePio";
}
novelService.selectPioAll()的内容,就是简单的查询表里全部字段
write方法,正常需要封装到util里,这里就随便放了
百分比可以做通过率或者什么比例的数据展示
public void write(List<Pio> pioList, String path) throws IOException {
// 1.创建一个工作簿
XSSFWorkbook xssfWorkbook = new XSSFWorkbook();
// 2.创建工作表
XSSFSheet sheet = xssfWorkbook.createSheet("商品");
// 3.创建行
XSSFRow row = sheet.createRow(0);
row.createCell(0).setCellValue("商品编号");
row.createCell(1).setCellValue("商品名称");
row.createCell(2).setCellValue("商品价格(单位:元/斤)");
row.createCell(3).setCellValue("商品库存(单位:吨)");
DecimalFormat df = new DecimalFormat("0.0");//设置保留位数,下面除法的百分比用到。直接用"/"是保留整数的除,"%"是取余,都不适用
for (int i = 0; i < pioList.size(); i++) {
XSSFRow row1 = sheet.createRow(i + 1);
row1.createCell(0).setCellValue(pioList.get(i).getPid());
row1.createCell(1).setCellValue(pioList.get(i).getPname());
row1.createCell(2).setCellValue(pioList.get(i).getPrice() + "/" + pioList.get(i).getPrice() + "(" + df.format((float)pioList.get(i).getPrice()/pioList.get(i).getPrice()*100) + "%" + ")");
row1.createCell(3).setCellValue(pioList.get(i).getPstock());
}
FileOutputStream fileOutputStream = new FileOutputStream(path);
xssfWorkbook.write(fileOutputStream);
fileOutputStream.flush();
fileOutputStream.close();
xssfWorkbook.close();
}
jsp里的内容
数据库的内容
在主界面随便找个地方作为入口
到主界面点进去
输入地址,这里输入在桌面创建一个product.xlsx
回到桌面查看
3.3 展示数据和一键导出为Excel表格
controller层
// 从数据库读取数据写到Excel中
@RequestMapping("/writePio")
public String writePio(String path, HttpServletResponse servletResponse, Model model) throws IOException {
/*FileSystemView fsv = FileSystemView.getFileSystemView(); //注意了,这里重要的一句
System.out.println(fsv.getHomeDirectory()); //得到桌面路径
path = fsv.getHomeDirectory() + "\\aaa.xlsx";*/
// 2.导出
// 2.1读取数据库中的数据
List<Pio> pioList = novelService.selectPioAll();
model.addAttribute("pioList", pioList);
// 2.2将数据写入到excel表格中
if(path != null) {
write(pioList, path);
download(path, servletResponse);
System.out.println("写入成功!");
}
return "writePio";
}
调用Uitl里的两个方法:write,download
write方法
public void write(List<Pio> pioList, String path) throws IOException {
// 1.创建一个工作簿
XSSFWorkbook xssfWorkbook = new XSSFWorkbook();
// 2.创建工作表
XSSFSheet sheet = xssfWorkbook.createSheet("商品");
// 3.创建行
XSSFRow row = sheet.createRow(0);
row.createCell(0).setCellValue("商品编号");
row.createCell(1).setCellValue("商品名称");
row.createCell(2).setCellValue("商品价格(单位:元/斤)");
row.createCell(3).setCellValue("商品库存(单位:吨)");
DecimalFormat df = new DecimalFormat("0.0");//设置保留位数,下面除法的百分比用到。直接用"/"是保留整数的除,"%"是取余,都不适用
for (int i = 0; i < pioList.size(); i++) {
XSSFRow row1 = sheet.createRow(i + 1);
row1.createCell(0).setCellValue(pioList.get(i).getPid());
row1.createCell(1).setCellValue(pioList.get(i).getPname());
row1.createCell(2).setCellValue(pioList.get(i).getPrice() + "/" + pioList.get(i).getPrice() + "(" + df.format((float)pioList.get(i).getPrice()/pioList.get(i).getPrice()*100) + "%" + ")");
row1.createCell(3).setCellValue(pioList.get(i).getPstock());
}
FileOutputStream fileOutputStream = new FileOutputStream(path);
xssfWorkbook.write(fileOutputStream);
fileOutputStream.flush();
fileOutputStream.close();
xssfWorkbook.close();
}
download方法
/**
* @param path 想要下载的文件的路径
* @param response
* @功能描述 下载文件: 将文件以流的形式一次性读取到内存,通过响应输出流输出到前端
*/
@RequestMapping("/download")
public void download(String path, HttpServletResponse response) {
try {
// path是指想要下载的文件的路径
File file = new File(path);
System.out.println(file.getPath());
// 获取文件名
String filename = file.getName();
// 获取文件后缀名
String ext = filename.substring(filename.lastIndexOf(".") + 1).toLowerCase();
System.out.println(("文件后缀名:" + ext));
// 将文件写入输入流
FileInputStream fileInputStream = new FileInputStream(file);
InputStream fis = new BufferedInputStream(fileInputStream);
byte[] buffer = new byte[fis.available()];
fis.read(buffer);
fis.close();
// 清空response
response.reset();
// 设置response的Header
response.setCharacterEncoding("UTF-8");
// Content-Disposition的作用:告知浏览器以何种方式显示响应返回的文件,用浏览器打开还是以附件的形式下载到本地保存
// attachment表示以附件方式下载 inline表示在线打开 "Content-Disposition: inline; filename=文件名.mp3"
// filename表示文件的默认名称,因为网络传输只支持URL编码的相关支付,因此需要将文件名URL编码后进行传输,前端收到后需要反编码才能获取到真正的名称
response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));
// 告知浏览器文件的大小
response.addHeader("Content-Length", "" + file.length());
OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
response.setContentType("application/octet-stream");
outputStream.write(buffer);
outputStream.flush();
} catch (IOException ex) {
ex.printStackTrace();
}
}
数据库
jsp页面
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%--
Created by IntelliJ IDEA.
User: ghost
Date: 2022/1/5
Time: 11:37
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>导出文档</title>
</head>
<body>
<%--展示查出的数据--%>
<table>
<tr>
<%--<c:forEach var="pioList" items="${pioList}" varStatus="s">--%>
<td style="padding-left: 40px">商品名称</td>
<td style="padding-left: 50px">商品价格(单位:元/斤)</td>
<td style="padding-left: 50px">商品库存(单位:吨)</td>
<%--</c:forEach>--%>
</tr>
</table>
<ul style="list-style:none;">
<c:forEach var="pioList" items="${pioList}" varStatus="s">
<li style="display: inline; padding-left: 10px; color: fuchsia">${pioList.pname}</li>
<li style="display: inline; padding-left: 120px; color: #ff9fa5">${pioList.price}</li>
<li style="display: inline; padding-left: 200px; color: #d4af7a">${pioList.pstock}</li>
<li> </li>
</c:forEach>
</ul>
<%--导出为Excel表格--%>
<form action="/novel/writePio">
<%--<label for="path" style="height: 30px; color: #03bbe8">文件名 : </label>
<input type="text" name="path" id="path" size="50px" style="height: 30px" placeholder="请输入文件名(以.xlsx结尾,否则导出的不是Excel格式):">--%>
<%--导出的默认文件名为商品信息.xlsx--%>
<select name="path" id="path" style="display:none">
<option>商品信息.xlsx</option>
</select>
<input type="submit" value="导出为Excel表格" style="width: 150px; height: 60px; margin-top: 10px; margin-left: 100px"/>
</form>
</body>
</html>
最终效果
点击“导出为Excel表格就可以导出”