在企业开发中,Excel报表是一种最常见的报表需求,Excel报表开发一般分为两种形式:
(1)为了方便操作,基于Excel的报表批量上传数据
(2)通过java代码,快速生成Excel报表
POI报表简介:
Apache POI是Apache软件基金会的开源项目,由Java编写的免费开源的跨平台的 Java API,Apache POI提供API给Java语言操作Microsoft Office的功能。
Excel分为两个大的版本Excel2003和Excel2007及以上两个版本:
区别 | Excel 2003 | Excel 2007及以上 |
---|---|---|
扩展名 | xls | xlsx |
数据结构 | 二进制格式 | xml格式 |
单sheet数据量 | 行:65535、列:256 | 行:1048576、列:16384 |
特点 | 存储容量有限 | 基于xml压缩,占用空间小,操作效率高 |
API对象介绍
工作簿(表): WorkBook (HssfWordBook:2003版本,XssfWorkBook:2007及以上)
页:Sheet
行:Row
单元格:Cell
1.创建项目,环境搭建,导入pom.xml的坐标文件
org.apache.poi
poi
4.0.1
org.apache.poi
poi-ooxml
4.0.1
org.apache.poi
poi-ooxml-schemas
4.0.1
2.通过java创建Excel
public class CreateDemo {
/*
通过java代码创建excel
*/
public static void main(String[] args)throws Exception {
// 1.创建工作表
/*
HSSFWorkbook:2003版本
XSSFWorkbook:2007及以上版本
SXSSFWorkbook:处理百万数据
*/
Workbook wb = new XSSFWorkbook();
// 2.创建第一页
Sheet sheet = wb.createSheet("poi入门");
// 指定第三列宽度 24
sheet.setColumnWidth(2, 24*256); // poi的设计者把我们设置的宽度 除以256
// 3.创建第三行
Row row = sheet.createRow(2);// 指定行索引行
// 4.创建第三个单元格
Cell cell = row.createCell(2);// 指定索引列
// 5.填充数据
cell.setCellValue("北京欢迎你");
// 6.保存到本地
FileOutputStream out = new FileOutputStream("d:/demo.xlsx");
wb.write(out);
}
}
3.通过java解析Excel
注意:excel单元格类型为:
string
number
boolean
date
public class ParseDemo {
/*
通过java代码解析excel
*/
public static void main(String[] args) throws IOException {
// 1.加载已有的excel工作表
Workbook wb = new XSSFWorkbook("C:\\Users\\wsl\\Desktop\\demo.xlsx");
// 2.获取第一页
Sheet sheet = wb.getSheetAt(0);
// 3.遍历行
// sheet.getLastRowNum(); 获取最后一个有数据行的索引号(索引是从0开始),返回值4
for (int i = 0; i < sheet.getLastRowNum() + 1; i++) {
Row row = sheet.getRow(i);
// 4.遍历单元格
// row.getLastCellNum(); 获取最后一个有数据列的编号(编号是从1开始),返回值6
for (int j = 2; j < row.getLastCellNum(); j++) {
Cell cell = row.getCell(j);
// 5.获取单元格的数据
// System.out.print(cell.getStringCellValue()+" ");
System.out.print(getCellValue(cell)+" ");
}
System.out.println(" ");// 换行
}
}
//解析每个单元格的数据
public static Object getCellValue(Cell cell) {
Object obj = null;
CellType cellType = cell.getCellType(); //获取单元格数据类型
switch (cellType) {
case STRING: {
obj = cell.getStringCellValue();//字符串
break;
}
//excel默认将日期也理解为数字
case NUMERIC: {
if (DateUtil.isCellDateFormatted(cell)) {
obj = cell.getDateCellValue();//日期
} else {
obj = cell.getNumericCellValue(); // 数字
}
break;
}
case BOOLEAN: {
obj = cell.getBooleanCellValue(); // 布尔
break;
}
default: {
break;
}
}
return obj;
}
}
按规定导出出货表,导出出货表的excel
自定义SQL语句:
-- 根据船期+企业id查询
SELECT
c.`custom_name` AS customName,
c.`contract_no` AS contractNo,
cp.`product_no` AS productNo,
cp.`cnumber`,
cp.`factory_name` AS factoryName,
c.`delivery_period` AS deliveryPeriod,
c.`ship_time` AS shipTime,
c.`trade_terms` AS tradeTerms
FROM `co_contract` c
INNER JOIN `co_contract_product` cp ON c.`id` = cp.`contract_id`
WHERE DATE_FORMAT(c.`ship_time`,'%Y-%m') = '2015-01' AND c.`company_id` = '1'
自定义VO对象
import java.io.Serializable;
import java.util.Date;
public class ContractProductVo implements Serializable {
private String customName; //客户名称
private String contractNo; //合同号,订单号
private String productNo; //货号
private Integer cnumber; //数量
private String factoryName; //厂家名称,冗余字段
private Date deliveryPeriod; //交货期限
private Date shipTime; //船期
private String tradeTerms; //贸易条款
public String getCustomName() {
return customName;
}
public void setCustomName(String customName) {
this.customName = customName;
}
public String getContractNo() {
return contractNo;
}
public void setContractNo(String contractNo) {
this.contractNo = contractNo;
}
public String getProductNo() {
return productNo;
}
public void setProductNo(String productNo) {
this.productNo = productNo;
}
public Integer getCnumber() {
return cnumber;
}
public void setCnumber(Integer cnumber) {
this.cnumber = cnumber;
}
public String getFactoryName() {
return factoryName;
}
public void setFactoryName(String factoryName) {
this.factoryName = factoryName;
}
public Date getDeliveryPeriod() {
return deliveryPeriod;
}
public void setDeliveryPeriod(Date deliveryPeriod) {
this.deliveryPeriod = deliveryPeriod;
}
public Date getShipTime() {
return shipTime;
}
public void setShipTime(Date shipTime) {
this.shipTime = shipTime;
}
public String getTradeTerms() {
return tradeTerms;
}
public void setTradeTerms(String tradeTerms) {
this.tradeTerms = tradeTerms;
}
@Override
public String toString() {
return "ContractProductVo{" +
"customName='" + customName + '\'' +
", contractNo='" + contractNo + '\'' +
", productNo='" + productNo + '\'' +
", cnumber=" + cnumber +
", factoryName='" + factoryName + '\'' +
", deliveryPeriod=" + deliveryPeriod +
", shipTime=" + shipTime +
", tradeTerms='" + tradeTerms + '\'' +
'}';
}
}
下载工具类:
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class DownloadUtils {
/**
* @param filePath 要下载的文件路径
* @param returnName 返回的文件名
* @param response HttpServletResponse
* @param delFlag 是否删除文件
*/
protected static void download(String filePath,String returnName,HttpServletResponse response,boolean delFlag){
prototypeDownload(new File(filePath), returnName, response, delFlag);
}
/**
* @param file 要下载的文件
* @param returnName 返回的文件名
* @param response HttpServletResponse
* @param delFlag 是否删除文件
*/
protected static void download(File file,String returnName,HttpServletResponse response,boolean delFlag){
prototypeDownload(file, returnName, response, delFlag);
}
/**
* @param file 要下载的文件
* @param returnName 返回的文件名
* @param response HttpServletResponse
* @param delFlag 是否删除文件
*/
public static void prototypeDownload(File file,String returnName,HttpServletResponse response,boolean delFlag){
// 下载文件
FileInputStream inputStream = null;
ServletOutputStream outputStream = null;
try {
if(!file.exists()) return;
response.reset();
//设置响应类型 PDF文件为"application/pdf",WORD文件为:"application/msword", EXCEL文件为:"application/vnd.ms-excel"。
response.setContentType("application/octet-stream;charset=utf-8");
//设置响应的文件名称,并转换成中文编码
//returnName = URLEncoder.encode(returnName,"UTF-8");
returnName = response.encodeURL(new String(returnName.getBytes(),"iso8859-1")); //保存的文件名,必须和页面编码一致,否则乱码
//attachment作为附件下载;inline客户端机器有安装匹配程序,则直接打开;注意改变配置,清除缓存,否则可能不能看到效果
response.addHeader("Content-Disposition", "attachment;filename="+returnName);
//将文件读入响应流
inputStream = new FileInputStream(file);
outputStream = response.getOutputStream();
int length = 1024;
int readLength=0;
byte buf[] = new byte[1024];
readLength = inputStream.read(buf, 0, length);
while (readLength != -1) {
outputStream.write(buf, 0, readLength);
readLength = inputStream.read(buf, 0, length);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
outputStream.flush();
} catch (IOException e) {
e.printStackTrace();
}
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
//删除原文件
if(delFlag) {
file.delete();
}
}
}
/**
* @param byteArrayOutputStream 将文件内容写入ByteArrayOutputStream
* @param response HttpServletResponse 写入response
* @param returnName 返回的文件名
*/
public static void download(ByteArrayOutputStream byteArrayOutputStream, HttpServletResponse response, String returnName) throws IOException{
response.setContentType("application/octet-stream;charset=utf-8");
returnName = response.encodeURL(new String(returnName.getBytes(),"iso8859-1")); //保存的文件名,必须和页面编码一致,否则乱码
response.addHeader("Content-Disposition", "attachment;filename=" + returnName);
response.setContentLength(byteArrayOutputStream.size());
ServletOutputStream outputstream = response.getOutputStream(); //取得输出流
byteArrayOutputStream.writeTo(outputstream); //写到输出流
byteArrayOutputStream.close(); //关闭
outputstream.flush(); //刷数据
}
}
基础数据:
@Controller
@RequestMapping(value = "/cargo/contract", name = "出货表导出")
public class OutContractController extends BaseController {
@Reference // alibaba
private ContractService contractService;
@RequestMapping(value = "/print", name = "跳转到查询页面")
public String print() {
return "cargo/print/contract-print";
}
@RequestMapping(value = "/printExcel", name = "出货表excel下载")
public void printExcel(String shipTime) {
//i.查询vo数据集合
List list = contractService.findByShipTime(shipTime,getCompanyId());
System.out.println(list.size());
//ii.将vo对象封装到excel文件中
//iii.完成文件下载
}
}
ContractService
// 根据船期查询出货表
@Override
public List findByShipTime(String shipTime, String companyId) {
return contractDao.findByShipTime(shipTime,companyId);
}
ContractDao
// 根据船期查询出货表
List findByShipTime(@Param("shipTime") String shipTime, @Param("companyId") String companyId);
导出样式:
Excel下载:
(1)不带样式:
@Controller
@RequestMapping(value = "/cargo/contract", name = "出货表导出")
public class OutContractController extends BaseController {
@Reference // alibaba
private ContractService contractService;
@RequestMapping(value = "/print", name = "跳转到查询页面")
public String print() {
return "cargo/print/contract-print";
}
@RequestMapping(value = "/printExcel", name = "出货表excel下载")
public void printExcel(String shipTime) throws Exception {
//i.查询vo数据集合
List list = contractService.findByShipTime(shipTime, getCompanyId());
System.out.println(list.size());
//ii.将vo对象封装到excel文件中
// 1.创建Workbook 2007版本
Workbook wb = new XSSFWorkbook();
// 2.创建第一页
Sheet sheet = wb.createSheet("Sheet1");
// 指定列宽度
sheet.setColumnWidth(1, 20 * 256);
sheet.setColumnWidth(2, 15 * 256);
sheet.setColumnWidth(3, 15 * 256);
sheet.setColumnWidth(4, 15 * 256);
sheet.setColumnWidth(5, 15 * 256);
sheet.setColumnWidth(6, 15 * 256);
sheet.setColumnWidth(7, 15 * 256);
// 合并单元格 行开始、行结束、列开始、列结束 :取值都是索引
sheet.addMergedRegion(new CellRangeAddress(0, 0, 1, 8));
// 3.第一行指定大标题
// 3.1 创建第一行
Row row = sheet.createRow(0);
row.setHeightInPoints(36);// 行高
// 3.2 创建第二个单元格
Cell cell = row.createCell(1);
// 3.3 填充大标题
String bigTile = shipTime.replace("-", "年").replace("年0", "年") + "月份出货表";
cell.setCellValue(bigTile);
// 4.第二行指定列标题
// 4.1 创建第二行
row = sheet.createRow(1);
row.setHeightInPoints(26);// 行高
// 4.2 指定列标题数组
String[] colTitile = new String[]{"", "客户", "合同号", "货号", "数量", "工厂", "工厂交期", "船期", "贸易条款"};
// 4.3 遍历数组设置列标题
for (int i = 1; i < colTitile.length; i++) {
cell = row.createCell(i);
cell.setCellValue(colTitile[i]);
}
// 5.从第三行开始,遍历vo数据集合,填充数据行
int index = 2;
for (ContractProductVo vo : list) {
row = sheet.createRow(index);
row.setHeightInPoints(24);// 行高
// 客户单元格
cell = row.createCell(1);
cell.setCellValue(vo.getCustomName());
// 合同号单元格
cell = row.createCell(2);
cell.setCellValue(vo.getContractNo());
// 货号单元格
cell = row.createCell(3);
cell.setCellValue(vo.getProductNo());
// 数量单元格
cell = row.createCell(4);
cell.setCellValue(vo.getCnumber());
// 工厂单元格
cell = row.createCell(5);
cell.setCellValue(vo.getFactoryName());
// 工厂交期单元格
cell = row.createCell(6);
cell.setCellValue(new SimpleDateFormat("yyyy-MM-dd").format(vo.getDeliveryPeriod()));
// 船期单元格
cell = row.createCell(7);
cell.setCellValue(new SimpleDateFormat("yyyy-MM-dd").format(vo.getShipTime()));
// 贸易条款单元格
cell = row.createCell(8);
cell.setCellValue(vo.getTradeTerms());
index++;
}
//iii.完成文件下载
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
// 将workbook的内容输出到字节数组输出中
wb.write(byteArrayOutputStream);
DownloadUtils.download(byteArrayOutputStream, response, bigTile + ".xlsx");
}
}
(2)带样式
@Controller
@RequestMapping(value = "/cargo/contract", name = "出货表导出")
public class OutContractController extends BaseController {
@Reference // alibaba
private ContractService contractService;
@RequestMapping(value = "/print", name = "跳转到查询页面")
public String print() {
return "cargo/print/contract-print";
}
@RequestMapping(value = "/printExcel", name = "出货表excel下载")
public void printExcel(String shipTime) throws Exception {
//i.查询vo数据集合
List list = contractService.findByShipTime(shipTime, getCompanyId());
System.out.println(list.size());
//ii.将vo对象封装到excel文件中
// 1.创建Workbook 2007版本
Workbook wb = new XSSFWorkbook();
// 2.创建第一页
Sheet sheet = wb.createSheet("Sheet1");
// 指定列宽度
sheet.setColumnWidth(1, 20 * 256);
sheet.setColumnWidth(2, 15 * 256);
sheet.setColumnWidth(3, 15 * 256);
sheet.setColumnWidth(4, 15 * 256);
sheet.setColumnWidth(5, 15 * 256);
sheet.setColumnWidth(6, 15 * 256);
sheet.setColumnWidth(7, 15 * 256);
// 合并单元格 行开始、行结束、列开始、列结束 :取值都是索引
sheet.addMergedRegion(new CellRangeAddress(0, 0, 1, 8));
// 3.第一行指定大标题
// 3.1 创建第一行
Row row = sheet.createRow(0);
row.setHeightInPoints(36);// 行高
// 3.2 创建第二个单元格
Cell cell = row.createCell(1);
// 3.3 填充大标题
String bigTile = shipTime.replace("-", "年").replace("年0", "年") + "月份出货表";
cell.setCellValue(bigTile);
cell.setCellStyle(bigTitle(wb)); // 大标题样式
// 4.第二行指定列标题
// 4.1 创建第二行
row = sheet.createRow(1);
row.setHeightInPoints(26);// 行高
// 4.2 指定列标题数组
String[] colTitile = new String[]{"", "客户", "合同号", "货号", "数量", "工厂", "工厂交期", "船期", "贸易条款"};
// 4.3 遍历数组设置列标题
for (int i = 1; i < colTitile.length; i++) {
cell = row.createCell(i);
cell.setCellValue(colTitile[i]);
cell.setCellStyle(title(wb)); // 列标题样式
}
// 5.从第三行开始,遍历vo数据集合,填充数据行
int index = 2;
for (ContractProductVo vo : list) {
row = sheet.createRow(index);
row.setHeightInPoints(24);// 行高
// 客户单元格
cell = row.createCell(1);
cell.setCellValue(vo.getCustomName());
cell.setCellStyle(text(wb)); // 数据样式
// 合同号单元格
cell = row.createCell(2);
cell.setCellValue(vo.getContractNo());
cell.setCellStyle(text(wb)); // 数据样式
// 货号单元格
cell = row.createCell(3);
cell.setCellValue(vo.getProductNo());
cell.setCellStyle(text(wb)); // 数据样式
// 数量单元格
cell = row.createCell(4);
cell.setCellValue(vo.getCnumber());
cell.setCellStyle(text(wb)); // 数据样式
// 工厂单元格
cell = row.createCell(5);
cell.setCellValue(vo.getFactoryName());
cell.setCellStyle(text(wb)); // 数据样式
// 工厂交期单元格
cell = row.createCell(6);
cell.setCellValue(new SimpleDateFormat("yyyy-MM-dd").format(vo.getDeliveryPeriod()));
cell.setCellStyle(text(wb)); // 数据样式
// 船期单元格
cell = row.createCell(7);
cell.setCellValue(new SimpleDateFormat("yyyy-MM-dd").format(vo.getShipTime()));
cell.setCellStyle(text(wb)); // 数据样式
// 贸易条款单元格
cell = row.createCell(8);
cell.setCellValue(vo.getTradeTerms());
cell.setCellStyle(text(wb)); // 数据样式
index++;
}
//iii.完成文件下载
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
// 将workbook的内容输出到字节数组输出中
wb.write(byteArrayOutputStream);
DownloadUtils.download(byteArrayOutputStream, response, bigTile + ".xlsx");
}
//大标题的样式
public CellStyle bigTitle(Workbook wb){
CellStyle style = wb.createCellStyle();
Font font = wb.createFont();
font.setFontName("宋体");
font.setFontHeightInPoints((short)16);
font.setBold(true);//字体加粗
style.setFont(font);
style.setAlignment(HorizontalAlignment.CENTER); //横向居中
style.setVerticalAlignment(VerticalAlignment.CENTER); //纵向居中
return style;
}
//小标题的样式
public CellStyle title(Workbook wb){
CellStyle style = wb.createCellStyle();
Font font = wb.createFont();
font.setFontName("黑体");
font.setFontHeightInPoints((short)12);
style.setFont(font);
style.setAlignment(HorizontalAlignment.CENTER); //横向居中
style.setVerticalAlignment(VerticalAlignment.CENTER); //纵向居中
style.setBorderTop(BorderStyle.THIN); //上细线
style.setBorderBottom(BorderStyle.THIN); //下细线
style.setBorderLeft(BorderStyle.THIN); //左细线
style.setBorderRight(BorderStyle.THIN); //右细线
return style;
}
//文字样式
public CellStyle text(Workbook wb){
CellStyle style = wb.createCellStyle();
Font font = wb.createFont();
font.setFontName("Times New Roman");
font.setFontHeightInPoints((short)10);
style.setFont(font);
style.setAlignment(HorizontalAlignment.LEFT); //横向居左
style.setVerticalAlignment(VerticalAlignment.CENTER); //纵向居中
style.setBorderTop(BorderStyle.THIN); //上细线
style.setBorderBottom(BorderStyle.THIN); //下细线
style.setBorderLeft(BorderStyle.THIN); //左细线
style.setBorderRight(BorderStyle.THIN); //右细线
return style;
}
}
自定义生成Excel报表文件还是有很多不尽如意的地方,特别是针对复杂报表头,单元格样式,字体等操作。手写这些代码不仅费时费力,有时候效果还不太理想。那怎么样才能更方便的对报表样式,报表头进行处理呢?
答案:使用已经准备好的Excel模板,开发者只需要关注模板中的数据即可。
提取模板:
模板打印
// 模板下载
@RequestMapping(value = "/printExcelTemplate", name = "出货表excel模板下载")
public void printExcelTemplate(String shipTime) throws Exception {
// i.查询vo数据集合
List list = contractService.findByShipTime(shipTime, getCompanyId());
// ii.加载模板,填充数据,生成excel
// 1.获取模板真实路径
String realPath = session.getServletContext().getRealPath("/make/xlsprint/tOUTPRODUCT.xlsx");
// 2.基于模板创建workbook
Workbook wb = new XSSFWorkbook(realPath);
// 3.获取第一页
Sheet sheet = wb.getSheetAt(0);
// 4.重置大标题数据
Row row = sheet.getRow(0);
Cell cell = row.getCell(1);
String bigTile = shipTime.replace("-", "年").replace("年0", "年") + "月份出货表";
cell.setCellValue(bigTile);
// 5.列标题(略)
// 6.抽取数据行的样式
// 6.1 获取第三行对象
row = sheet.getRow(2);
// 6.2 定义样式存储数组
CellStyle[] css = new CellStyle[9]; // null,客人,合同号,货号
// 6.3 遍历第三行
for (int i = 1; i < row.getLastCellNum(); i++) {
// 获取单元格对象
cell = row.getCell(i);
// 获取样式,并设置到数组
css[i] = cell.getCellStyle();
}
// 7.遍历vo数据集合,填充数据
int index = 2;
for (ContractProductVo vo : list) {
row = sheet.createRow(index);
// 客户单元格
cell = row.createCell(1);
cell.setCellValue(vo.getCustomName());
cell.setCellStyle(css[1]); // 数据样式
// 合同号单元格
cell = row.createCell(2);
cell.setCellValue(vo.getContractNo());
cell.setCellStyle(css[2]); // 数据样式
// 货号单元格
cell = row.createCell(3);
cell.setCellValue(vo.getProductNo());
cell.setCellStyle(css[3]); // 数据样式
// 数量单元格
cell = row.createCell(4);
cell.setCellValue(vo.getCnumber());
cell.setCellStyle(css[4]); // 数据样式
// 工厂单元格
cell = row.createCell(5);
cell.setCellValue(vo.getFactoryName());
cell.setCellStyle(css[5]); // 数据样式
// 工厂交期单元格
cell = row.createCell(6);
cell.setCellValue(new SimpleDateFormat("yyyy-MM-dd").format(vo.getDeliveryPeriod()));
cell.setCellStyle(css[6]); // 数据样式
// 船期单元格
cell = row.createCell(7);
cell.setCellValue(new SimpleDateFormat("yyyy-MM-dd").format(vo.getShipTime()));
cell.setCellStyle(css[7]); // 数据样式
// 贸易条款单元格
cell = row.createCell(8);
cell.setCellValue(vo.getTradeTerms());
cell.setCellStyle(css[8]); // 数据样式
index++;
}
//iii.完成文件下载
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
// 将workbook的内容输出到字节数组输出中
wb.write(byteArrayOutputStream);
DownloadUtils.download(byteArrayOutputStream, response, bigTile + ".xlsx");
}
总结:
加载模板
重置模板静态数据(大标题)
抽取模板样式(数据行)
填充vo数据
(1)概述
在真实的环境下,我们导出导入的数据量在几十万甚至上百万条,Excel 2007虽然可以支持1048576条数据存储;但实际运行时还可能存在问题,原因是执行POI报表所产生的行对象,单元格对象,字体对象,他们都不会销毁,这就导致OOM(内存溢出)的风险。
JDK性能监控工具
这里我们使用JDK提供的性能工具Jvisualvm来监视程序的运行情况,包括CPU,垃圾回收,内存的分配和使用情况,这让程序的运行阶段变得更加可控。
Jvisualvm工具位于JAVA_HOME/bin目录下,直接双击就可以打开该程序。
百万数据报表导出
SxssfWorkBookx对象:是用来生成海量excel数据文件,主要原理是借助临时存储空间生成excel(当内存中的对象数量到达一定数量后,写入到临时文件,销毁内存对象,直至Excel导出完成)
缺点
不能使用模板
样式对象不能超过64000个
注意:
比较:
使用:XSSFWorkbook对象,造成内存一致增长
使用:SXSSFWorkbook对象,借助临时文件,内存有增有减
(2)百万数据报表导入
**POI针对Excel文件解析提供了两种方式:**
用户模式:通过POI提供的基础方法完成excel解析,操作简单,但创建太多的对象,消耗内存
事件模式:基于SAX方式解析XML,它逐行扫描文档,一边扫描,一边解析。
通过用户模式解析:即Dom方式
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.awt.*;
import java.io.IOException;
public class ParseDemo {
/*
通过java代码解析excel
*/
public static void main(String[] args) throws IOException {
// 1.加载已有的excel工作表
Workbook wb = new XSSFWorkbook("C:\\\\Users\\\\Desktop\\\\2020年2月份出货表.xlsx");
// 2.获取第一页
Sheet sheet = wb.getSheetAt(0);
// 3.遍历行
// sheet.getLastRowNum(); 获取最后一个有数据行的索引号(索引是从0开始),返回值4
for (int i = 0; i < sheet.getLastRowNum() + 1; i++) {
Row row = sheet.getRow(i);
// 4.遍历单元格
// row.getLastCellNum(); 获取最后一个有数据列的编号(编号是从1开始),返回值6
for (int j = 2; j < row.getLastCellNum(); j++) {
Cell cell = row.getCell(j);
// 5.获取单元格的数据
// System.out.print(cell.getStringCellValue()+" ");
System.out.print(getCellValue(cell)+" ");
}
System.out.println(" ");// 换行
}
}
//解析每个单元格的数据
public static Object getCellValue(Cell cell) {
Object obj = null;
CellType cellType = cell.getCellType(); //获取单元格数据类型
switch (cellType) {
case STRING: {
obj = cell.getStringCellValue();//字符串
break;
}
//excel默认将日期也理解为数字
case NUMERIC: {
if (DateUtil.isCellDateFormatted(cell)) {
obj = cell.getDateCellValue();//日期
} else {
obj = cell.getNumericCellValue(); // 数字
}
break;
}
case BOOLEAN: {
obj = cell.getBooleanCellValue(); // 布尔
break;
}
default: {
break;
}
}
return obj;
}
}
通过SAX方式解析:重要:
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.openxml4j.opc.PackageAccess;
import org.apache.poi.xssf.eventusermodel.XSSFReader;
import org.apache.poi.xssf.eventusermodel.XSSFSheetXMLHandler;
import org.apache.poi.xssf.model.SharedStringsTable;
import org.apache.poi.xssf.model.StylesTable;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;
import java.io.InputStream;
public class ExcelParse {
public static void parse(String path) throws Exception {
//解析器
SheetHandler hl = new SheetHandler();
//1.根据 Excel 获取 OPCPackage 对象
OPCPackage pkg = OPCPackage.open(path, PackageAccess.READ);
try {
//2.创建 XSSFReader 对象
XSSFReader reader = new XSSFReader(pkg);
//3.获取 SharedStringsTable 对象
SharedStringsTable sst = reader.getSharedStringsTable();
//4.获取 StylesTable 对象
StylesTable styles = reader.getStylesTable();
XMLReader parser = XMLReaderFactory.createXMLReader();
// 处理公共属性
parser.setContentHandler(new XSSFSheetXMLHandler(styles, sst, hl,
false));
XSSFReader.SheetIterator sheets = (XSSFReader.SheetIterator)
reader.getSheetsData();
//逐行读取逐行解析
while (sheets.hasNext()) {
InputStream sheetstream = sheets.next();
InputSource sheetSource = new InputSource(sheetstream);
try {
parser.parse(sheetSource);
} finally {
sheetstream.close();
}
}
} finally {
pkg.close();
}
}
public static void main(String[] args) throws Exception {
parse("C:\\Users\\Desktop\\2020年2月份出货表.xlsx");
}
}
import org.apache.poi.xssf.eventusermodel.XSSFSheetXMLHandler;
import org.apache.poi.xssf.usermodel.XSSFComment;
public class SheetHandler implements XSSFSheetXMLHandler.SheetContentsHandler {
// 行解析开始
@Override
public void startRow(int rowNum) {
if (rowNum > 2) {
System.out.println("开启数据解析");
}
}
// 行解析结束
@Override
public void endRow(int rowNum) {
System.out.println(""); // 手动换行
}
// 获取单元格数据
/*
cellReference:单元格编号(C3、D5、E8),可以判断数据的类型,方便取值
formattedValue:单元格数据(内容)
comment:单元格批注(注释)
*/
@Override
public void cell(String cellReference, String formattedValue, XSSFComment comment) {
System.out.print(formattedValue+" ");
}
}
pom.xml
org.apache.poi
poi
4.0.1
org.apache.poi
poi-ooxml
4.0.1
org.apache.poi
poi-ooxml-schemas
4.0.1
org.apache.maven.plugins
maven-compiler-plugin
3.1
1.8
UTF-8
EasyPOI
easypoi功能如同名字easy,主打的功能就是容易,让一个没见接触过poi的人员,就可以方便的写出Excel导出,Excel模板导出,Excel导入,Word模板导出,通过简单的注解和模板语言(熟悉的表达式语法),完成以前复杂的写法。
入门案例:
cn.afterturn
easypoi-base
3.2.0
cn.afterturn
easypoi-web
3.2.0
cn.afterturn
easypoi-annotation
3.2.0
定义实体
public class ContractProductVo implements Serializable {
@Excel(name = "客户")
private String customName; //客户名称
@Excel(name = "合同号")
private String contractNo; //合同号,订单号
@Excel(name = "货号")
private String productNo; //货号
@Excel(name = "数量",type = 10)
private Integer cnumber; //数量
@Excel(name = "工厂")
private String factoryName; //厂家名称
@Excel(name = "工厂交期", format = "yyyy-MM-dd",width = 15)
private Date deliveryPeriod; //交货期限
@Excel(name = "船期", format = "yyyy-MM-dd",width = 15)
private Date shipTime; //船期
@Excel(name = "贸易条款")
private String tradeTerms; //贸易条款
public String getCustomName() {
return customName;
}
public void setCustomName(String customName) {
this.customName = customName;
}
public String getContractNo() {
return contractNo;
}
public void setContractNo(String contractNo) {
this.contractNo = contractNo;
}
public String getProductNo() {
return productNo;
}
public void setProductNo(String productNo) {
this.productNo = productNo;
}
public Integer getCnumber() {
return cnumber;
}
public void setCnumber(Integer cnumber) {
this.cnumber = cnumber;
}
public String getFactoryName() {
return factoryName;
}
public void setFactoryName(String factoryName) {
this.factoryName = factoryName;
}
public Date getDeliveryPeriod() {
return deliveryPeriod;
}
public void setDeliveryPeriod(Date deliveryPeriod) {
this.deliveryPeriod = deliveryPeriod;
}
public Date getShipTime() {
return shipTime;
}
public void setShipTime(Date shipTime) {
this.shipTime = shipTime;
}
public String getTradeTerms() {
return tradeTerms;
}
public void setTradeTerms(String tradeTerms) {
this.tradeTerms = tradeTerms;
}
@Override
public String toString() {
return "ContractProductVo{" +
"customName='" + customName + '\'' +
", contractNo='" + contractNo + '\'' +
", productNo='" + productNo + '\'' +
", cnumber=" + cnumber +
", factoryName='" + factoryName + '\'' +
", deliveryPeriod=" + deliveryPeriod +
", shipTime=" + shipTime +
", tradeTerms='" + tradeTerms + '\'' +
'}';
}
}
创建excel
public class CreateExcel {
public static void main(String[] args)throws Exception {
// 1.定义excel配置
ExportParams exportParams = new ExportParams();
exportParams.setSheetName("Sheet1");// 页名称
exportParams.setTitle("2015年01月份出货表");// 大标题
exportParams.setType(ExcelType.XSSF);// 07及以上版本
// 2.创建workbook对象
Workbook wb = ExcelExportUtil.exportExcel(exportParams, ContractProductVo.class, getData());
// 3.导出到文件
FileOutputStream out = new FileOutputStream("d:/demo.xlsx");
wb.write(out);
wb.close();
}
//从数据库中查找了数据
public static List getData() {
List list = new ArrayList<>();
for (int i = 0; i < 5; i++) {
ContractProductVo vo = new ContractProductVo();
vo.setCustomName("客户"+i);
vo.setContractNo("合同号"+i);
vo.setProductNo("货号"+i);
vo.setCnumber(i);
vo.setFactoryName("工厂"+i);
vo.setDeliveryPeriod(new Date());
vo.setShipTime(new Date());
vo.setTradeTerms("条款"+i);
list.add(vo);
}
return list;
}
}
解析Excel
public class ParseExcel {
public static void main(String[] args) {
// 1.解析excel的配置参数
ImportParams params = new ImportParams();
params.setTitleRows(1); // 大标题行数
params.setHeadRows(1); // 列标题行数
// 2.实现解析
List list = ExcelImportUtil.importExcel(new File("D:/demo.xlsx"), ContractProductVo.class, params);
// 3.遍历输出
for (ContractProductVo contractProductVo : list) {
System.out.println(contractProductVo);
}
}
}
easyExcel
JAVA解析Excel工具EasyExcel
Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。easyexcel重写了poi对07版Excel的解析,能够原本一个3M的excel用POI sax依然需要100M左右内存降低到几M,并且再大的excel不会出现内存溢出,03版依赖POI的sax模式。在上层做了模型转换的封装,让使用者更加简单方便
EasyExcel文档
EasyExcel 入口类,用于构建开始各种操作