1.POI入门教程链接
http://t.csdnimg.cn/Axn4Phttp://t.csdnimg.cn/Axn4P建议:从入门看起会更好理解POI对Excel数据的使用和处理
记得引入依赖:
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-schemas</artifactId>
<version>4.0.1</version>
</dependency>
2.POI对单元格样式处理
// JUnit的@Test注解表示这是一个测试方法
@Test
void testExcel() throws IOException {
// 创建一个新的XSSFWorkbook对象,这是操作Excel文件的核心类
XSSFWorkbook workbook = new XSSFWorkbook();
// 在工作簿中创建一个名为"sheet1"的工作表
XSSFSheet sheet = workbook.createSheet("sheet1");
// 在工作表中创建第三行(行索引从0开始)
Row row = sheet.createRow(2);
// 在该行中创建第三个单元格(列索引从0开始)
Cell cell = row.createCell(2);
// 设置单元格的值为"Hello World 2024"
cell.setCellValue("Hello World 2024");
// 创建一个单元格样式
CellStyle cellStyle = workbook.createCellStyle();
// 设置单元格的上下左右边框为细线
cellStyle.setBorderTop(BorderStyle.THIN);
cellStyle.setBorderBottom(BorderStyle.THIN);
cellStyle.setBorderLeft(BorderStyle.THIN);
cellStyle.setBorderRight(BorderStyle.THIN);
// 创建一个字体对象
Font font = workbook.createFont();
// 设置字体为"宋体"
font.setFontName("宋体");
// 设置字体大小为32磅
font.setFontHeightInPoints((short) 32);
// 将字体应用到单元格样式中
cellStyle.setFont(font);
// 设置行高为50磅
row.setHeightInPoints((short) 50);
// 设置第三列的宽度为100 * 365(单位是1/256个字符宽度)
sheet.setColumnWidth(2, 100 * 365);
// 将单元格样式应用到单元格上
cell.setCellStyle(cellStyle);
// 创建一个文件输出流,用于将Excel文件写入磁盘
FileOutputStream out = new FileOutputStream("D:\\Desktop\\JavaCode\\poi-demo\\src\\main\\resources\\test.xlsx");
// 将工作簿写入输出流
workbook.write(out);
// 关闭输出流
out.close();
}
3.将图片写入Excel表格中
// JUnit的@Test注解表示这是一个测试方法
@Test
void testDrawExcel() throws IOException {
// 创建一个新的XSSFWorkbook对象,这是操作Excel文件的核心类
XSSFWorkbook workbook = new XSSFWorkbook();
// 在工作簿中创建一个名为"sheet1"的工作表
XSSFSheet sheet = workbook.createSheet("sheet1");
// 创建一个文件输入流,用于读取图片文件
FileInputStream in = new FileInputStream("D:\\Desktop\\test.jpg");
// 创建一个字节数组,长度为图片文件的大小
byte[] bytes = new byte[in.available()];
// 读取图片文件到字节数组
in.read(bytes);
// 关闭输入流
in.close();
// 将图片添加到工作簿,并返回图片的索引
int index = workbook.addPicture(bytes, Workbook.PICTURE_TYPE_JPEG);
// 获取创建助手,用于创建绘图对象
CreationHelper helper = workbook.getCreationHelper();
// 创建绘图对象,用于在工作表中绘制图片
XSSFDrawing drawing = sheet.createDrawingPatriarch();
// 创建客户端锚点,用于指定图片的位置
ClientAnchor anchor = helper.createClientAnchor();
// 设置图片的起始行
anchor.setRow1(0);
// 设置图片的起始列
anchor.setCol1(1);
// 创建图片对象,并将其插入到工作表中
XSSFPicture picture = drawing.createPicture(anchor, index);
// 调整图片大小以适应单元格
picture.resize();
// 创建一个文件输出流,用于将Excel文件写入磁盘
FileOutputStream out = new FileOutputStream("D:\\Desktop\\JavaCode\\poi-demo\\src\\main\\resources\\test.xlsx");
// 将工作簿写入输出流
workbook.write(out);
// 关闭输出流
out.close();
}
4.使用模版Excel
如何想根据模版进行创建Excel表格。而不是直接覆盖创建新的Excel表格的方式:
// JUnit的@Test注解表示这是一个测试方法
@Test
void testDrawExcel() throws IOException {
// 使用Spring的ClassPathResource来加载类路径下的资源文件
Resource resource = new ClassPathResource("test.xlsx");
// 获取资源文件的FileInputStream
FileInputStream resourceIn = new FileInputStream(resource.getFile());
// 使用FileInputStream加载现有工作簿
XSSFWorkbook workbook = new XSSFWorkbook(resourceIn);
// 获取工作簿中的第一个工作表
XSSFSheet sheet = workbook.getSheetAt(0);
// 创建一个文件输入流,用于读取图片文件
FileInputStream in = new FileInputStream("D:\\Desktop\\test.jpg");
// 创建一个字节数组,长度为图片文件的大小
byte[] bytes = new byte[in.available()];
// 读取图片文件到字节数组
in.read(bytes);
// 关闭输入流
in.close();
// 将图片添加到工作簿,并返回图片的索引
int index = workbook.addPicture(bytes, Workbook.PICTURE_TYPE_JPEG);
// 获取创建助手,用于创建绘图对象
CreationHelper helper = workbook.getCreationHelper();
// 获取工作表中的绘图对象,如果没有则创建
Drawing<?> drawing = sheet.createDrawingPatriarch();
// 创建客户端锚点,用于指定图片的位置
ClientAnchor anchor = helper.createClientAnchor();
// 设置图片的起始行为第11行(行索引从0开始)
anchor.setRow1(10);
// 设置图片的起始列为第2列(列索引从0开始)
anchor.setCol1(1);
// 创建图片对象,并将其插入到工作表中
Picture picture = drawing.createPicture(anchor, index);
// 调整图片大小以适应单元格
picture.resize();
// 创建一个文件输出流,用于将Excel文件写入磁盘
FileOutputStream out = new FileOutputStream("D:\\Desktop\\JavaCode\\poi-demo\\src\\main\\resources\\test.xlsx");
// 将工作簿写入输出流
workbook.write(out);
// 关闭输出流
out.close();
}
5.自定义Excel工具类
(1)自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ExcelAttribute {
/** 对应的列名称 */
String name() default "";
/** 列序号 */
int sort();
/** 字段类型对应的格式 */
String format() default "";
}
(2) 导出工具类:
public class ExcelExportUtil<T> {
private int rowIndex;
private int styleIndex;
private String templatePath;
private Class clazz;
private Field fields[];
public ExcelExportUtil(Class clazz,int rowIndex,int styleIndex) {
this.clazz = clazz;
this.rowIndex = rowIndex;
this.styleIndex = styleIndex;
fields = clazz.getDeclaredFields();
}
/**
* 基于注解导出
*/
public void export(HttpServletResponse response,InputStream is, List<T> objs,String
fileName) throws Exception {
XSSFWorkbook workbook = new XSSFWorkbook(is);
Sheet sheet = workbook.getSheetAt(0);
CellStyle[] styles = getTemplateStyles(sheet.getRow(styleIndex));
AtomicInteger datasAi = new AtomicInteger(rowIndex);
for (T t : objs) {
Row row = sheet.createRow(datasAi.getAndIncrement());
for(int i=0;i<styles.length;i++) {
Cell cell = row.createCell(i);
cell.setCellStyle(styles[i]);
for (Field field : fields) {
if(field.isAnnotationPresent(ExcelAttribute.class)){
field.setAccessible(true);
ExcelAttribute ea = field.getAnnotation(ExcelAttribute.class);
if(i == ea.sort()) {
cell.setCellValue(field.get(t).toString());
}
}
}
}
}
fileName = URLEncoder.encode(fileName, "UTF-8");
response.setContentType("application/octet-stream");
response.setHeader("content-disposition", "attachment;filename=" + new
String(fileName.getBytes("ISO8859-1")));
response.setHeader("filename", fileName);
workbook.write(response.getOutputStream());
}
public CellStyle[] getTemplateStyles(Row row) {
CellStyle [] styles = new CellStyle[row.getLastCellNum()];
for(int i=0;i<row.getLastCellNum();i++) {
styles[i] = row.getCell(i).getCellStyle();
}
return styles;
}
}
(3)导入工具类:
public class ExcelImportUtil<T> {
private Class clazz;
private Field fields[];
public ExcelImportUtil(Class clazz) {
this.clazz = clazz;
fields = clazz.getDeclaredFields();
}
/**
* 基于注解读取excel
*/
public List<T> readExcel(InputStream is, int rowIndex,int cellIndex) {
List<T> list = new ArrayList<T>();
T entity = null;
try {
XSSFWorkbook workbook = new XSSFWorkbook(is);
Sheet sheet = workbook.getSheetAt(0);
// 不准确
int rowLength = sheet.getLastRowNum();
System.out.println(sheet.getLastRowNum());
for (int rowNum = rowIndex; rowNum <= sheet.getLastRowNum(); rowNum++) {
Row row = sheet.getRow(rowNum);
entity = (T) clazz.newInstance();
System.out.println(row.getLastCellNum());
for (int j = cellIndex; j < row.getLastCellNum(); j++) {
Cell cell = row.getCell(j);
for (Field field : fields) {
if(field.isAnnotationPresent(ExcelAttribute.class)){
field.setAccessible(true);
ExcelAttribute ea =
field.getAnnotation(ExcelAttribute.class);
if(j == ea.sort()) {
field.set(entity, covertAttrType(field, cell));
}
}
}
}
list.add(entity);
}
} catch (Exception e) {
e.printStackTrace();
}
return list;
}
/**
* 类型转换 将cell 单元格格式转为 字段类型
*/
private Object covertAttrType(Field field, Cell cell) throws Exception {
String fieldType = field.getType().getSimpleName();
if ("String".equals(fieldType)) {
return getValue(cell);
}else if ("Date".equals(fieldType)) {
return new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").parse(getValue(cell)) ;
}else if ("int".equals(fieldType) || "Integer".equals(fieldType)) {
return Integer.parseInt(getValue(cell));
}else if ("double".equals(fieldType) || "Double".equals(fieldType)) {
return Double.parseDouble(getValue(cell));
}else {
return null;
}
}
/**
* 格式转为String
* @param cell
* @return
*/
public String getValue(Cell cell) {
if (cell == null) {
return "";
}
switch (cell.getCellType()) {
case STRING:
return cell.getRichStringCellValue().getString().trim();
case NUMERIC:
if (DateUtil.isCellDateFormatted(cell)) {
Date dt = DateUtil.getJavaDate(cell.getNumericCellValue());
return new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(dt);
} else {
// 防止数值变成科学计数法
String strCell = "";
Double num = cell.getNumericCellValue();
BigDecimal bd = new BigDecimal(num.toString());
if (bd != null) {
strCell = bd.toPlainString();
}
// 去除 浮点型 自动加的 .0
if (strCell.endsWith(".0")) {
strCell = strCell.substring(0, strCell.indexOf("."));
}
return strCell;
}
case BOOLEAN:
return String.valueOf(cell.getBooleanCellValue());
default:
return "";
}
}
(4)工具类的使用
在User实体上对导出的字段名加上自定义注解@ExcelAttribute(序列号)
导入数据:
List<User> list = new ExcelImportUtil(User.class).readExcel(is, 1, 2);
导出数据:
@GetMapping("/export/{month}")
public void export(@PathVariable(name = "month") String month) throws Exception {
//1.构造数据
List<EmployeeReportResult> list =
userCompanyPersonalService.findByReport(companyId,month+"%");
//2.加载模板流数据
Resource resource = new ClassPathResource("excel-template/hr-demo.xlsx");
FileInputStream fis = new FileInputStream(resource.getFile());
new ExcelExportUtil(EmployeeReportResult.class,2,2).
export(response,fis,list,"人事报表.xlsx");
}
6.处理大量数据的文件(SXSSF)
(1)SXSSF和XSSF的区别
SXSSF和XSSF都是Apache POI库中用于处理Excel文件的API,它们属于POI项目中用于操作Excel 2007 OOXML (.xlsx)文件的组件。以下是SXSSF和XSSF的主要区别:
-
内存使用:
- XSSF:为处理大型Excel文件提供了完整的API支持,但它会将整个工作簿加载到内存中。这意味着对于非常大的Excel文件,它可能会消耗大量的Java堆内存。
- SXSSF:是XSSF的流式扩展,目的是减少内存消耗。它不会将整个工作簿保留在内存中,而是仅保持一定数量的行在内存中,其余的行将被写入磁盘上的临时文件中。
-
性能:
- 使用XSSF处理非常大的Excel文件时,可能会遇到性能瓶颈,因为它需要更多的内存资源。
- SXSSF在处理大型文件时性能更优,因为它使用了更少的内存,并且它的写入操作是流式的。
-
功能限制:
- XSSF提供了完整的Excel文件处理能力,但由于其内存占用较大,处理大型文件时可能会出现问题。
- SXSSF由于使用了流式处理,因此有一些限制。例如,它不支持获取或设置单元格样式,也不支持插入或删除行。
-
使用场景:
- 如果您的应用程序需要处理中等大小的Excel文件,并且需要完整的Excel功能,XSSF是一个不错的选择。
- 如果您需要处理大型Excel文件,并且可以接受SXSSF的一些限制,那么SXSSF可能是更好的选择。
(2)代码实现:
自定义处理器
/自定义Sheet基于Sax的解析处理器
public class SheetHandler implements XSSFSheetXMLHandler.SheetContentsHandler {
//封装实体对象
private PoiEntity entity;
/**
* 解析行开始
*/
@Override
public void startRow(int rowNum) {
if (rowNum >0 ) {
entity = new PoiEntity();
}
}
/* * 解析每一个单元格
*/
@Override
public void cell(String cellReference, String formattedValue, XSSFComment comment)
{
if(entity != null) {
switch (cellReference.substring(0, 1)) {
case "A":
entity.setId(formattedValue);
break;
case "B":
entity.setBreast(formattedValue);
break;
case "C":
entity.setAdipocytes(formattedValue);
break;
case "D":
entity.setNegative(formattedValue);
break;
case "E":
entity.setStaining(formattedValue);
break;
case "F":
entity.setSupportive(formattedValue);
break;
default:
break;
}
}
}
/**
* 解析行结束
*/
public void endRow(int rowNum) {
System.out.println(entity);
}
//处理头尾
public void headerFooter(String text, boolean isHeader, String tagName) {
}
}
自定义解析器
/**
* 自定义Excel解析器
*/
public class ExcelParser {
public void parse (String path) throws Exception {
//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();
//5.创建Sax的XmlReader对象
XMLReader parser = XMLReaderFactory.createXMLReader();
//6.设置处理器
parser.setContentHandler(new XSSFSheetXMLHandler(styles,sst, new
SheetHandler(), false));
XSSFReader.SheetIterator sheets = (XSSFReader.SheetIterator)
reader.getSheetsData();
//7.逐行读取
while (sheets.hasNext()) {
InputStream sheetstream = sheets.next();
InputSource sheetSource = new InputSource(sheetstream);
try {
parser.parse(sheetSource);
} finally {
sheetstream.close();
}
}
} finally {
pkg.close();
}
}
}