Poi模板技术

2024-08-26 14:48
文章标签 模板 技术 poi

本文主要是介绍Poi模板技术,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Poi模板技术导入导出Excel

在我们的工作中, 也经常会用到Poi模板技术操作Excel文件数据, 而且不同的应用场景对Excel表格的单元格格式和合并操作都需要写大量的代码调试, 比较麻烦. 通过模板技术将自定义格式的Excel文件模板存放在服务器指定位置, 然后读取数据或者填充数据都使用该模板的样式, 不用自己去编写代码设置样式, 使用非常方便. 同样的, 本篇不会写入门案例, 只是记录自己工作或学习中封装的工具方法.

说明: 以下代码基于poi-3.17版本实现, poi-3.17及以上版本相比3.17以下版本样式设置的api改动比较大, 可能存在数据类型获取api过时或报错等, 请参考 Poi版本升级优化

1. 自定义注解

借鉴EasyPoiEasyExcel的使用方式, 都通过注解开发方法来导出指定的字段或读取指定的属性数据. 下面我也自定义了一个简单的注解类.

com.poi.anno.ExcelAttr

package com.poi.anno;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 自定义注解, 用于标识需要导出到Excel文件的属性字段*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ExcelAttr {int sort() default 0;
}

2. 定义数据模型

将导入或导出的数据与pojo实体类关联, 因为要写getter, setter方法, 我使用lombok自动生成.

lombok依赖

<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.20</version><optional>true</optional>
</dependency>

com.poi.entity.Employee

package com.poi.entity;import com.poi.anno.ExcelAttr;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;/*** 类描述:数据模型, 员工信息* @Author wang_qz* @Date 2021/8/14 10:11* @Version 1.0*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Employee {@ExcelAttr(sort = 0)private int id;@ExcelAttr(sort = 1)private String name;@ExcelAttr(sort = 2)private String empno;@ExcelAttr(sort = 3)private int age;@ExcelAttr(sort = 4)private String sex;@ExcelAttr(sort = 5)private String job;@ExcelAttr(sort = 6)private int departid;
}

3. 大数据模板导入

3.1 导入Excel公共方法

com.poi.util.PoiTemplateUtil#importExcel

public List<T> importExcel(Map<String, Object> params, InputStream inputStream, Class<T> clazz) throws Exception {String fileType = (String) params.get("fileType");fileType = Objects.equals("xls", fileType) ? fileType : "xlsx";Workbook workbook = Objects.equals("xls", fileType) ? new HSSFWorkbook(inputStream) : new XSSFWorkbook(inputStream);Sheet sheet = workbook.getSheetAt(0);int lastRowNum = sheet.getLastRowNum();List<T> dataList = new ArrayList<>();for (int i = 1; i <= lastRowNum; i++) {Row row = sheet.getRow(i);short lastCellNum = row.getLastCellNum();// 获取单元格的值并存入objs中Object[] objs = new Object[lastCellNum];for (int j = 0; j < lastCellNum; j++) {Cell cell = row.getCell(j);Object value = getCellValue(cell);objs[j] = value;}T t = clazz.newInstance();// 反射设置属性值Field[] declaredFields = clazz.getDeclaredFields();for (int k = 0; k < declaredFields.length; k++) {Field field = declaredFields[k];field.setAccessible(true);// 获取被自定义注解ExcelAttr标识的字段ExcelAttr anno = field.getAnnotation(ExcelAttr.class);if (anno != null) {// 获取设置的字段排序int sort = anno.sort();if (sort == k) {String fieldType = field.getType().getName();final Object value = objs[k];if (Objects.equals("int", fieldType) || Objects.equals("Integer", fieldType)) {field.setInt(t, Integer.parseInt(value.toString()));} else if (Objects.equals("double", fieldType) ||Objects.equals("Double", fieldType)) {field.setDouble(t, Double.parseDouble(value.toString()));} else {field.set(t, value);}}}}dataList.add(t);}return dataList;
}

3.2 获取单元格数据类型

com.poi.util.PoiTemplateUtil#getCellValue

/*** 获取单元格的不同数据类型的值* @param cell* @return*/
public static Object getCellValue(Cell cell) {Object cellValue = ""; // 每个单元格的值if (cell != null) {// 数据类型判断, 获取不同类型的数据switch (cell.getCellTypeEnum()) {case NUMERIC: // 数字类型// 日期格式的数字if (HSSFDateUtil.isCellDateFormatted(cell)) {// 日期cellValue = cell.getDateCellValue();} else {// 不是日期格式的数字, 返回字符串格式cellValue = cell.getRichStringCellValue().toString();}break;case STRING: // 字符串cellValue = cell.getStringCellValue();break;case BOOLEAN: // 布尔cellValue = cell.getBooleanCellValue();break;case FORMULA: // 公式cellValue = String.valueOf(cell.getCellFormula());break;case BLANK: // 空cellValue = "";break;case ERROR: // 错误cellValue = "非法字符";break;default:cellValue = "未知类型";break;}}return cellValue;
}

4. 大数据模板导出

com.poi.util.PoiTemplateUtil#exportExcel

/*** 根据模板填充数据生成excel文件* @param params* @param outputStream* @throws Exception*/
public void exportExcel(@NonNull Map<String, Object> params, OutputStream outputStream) throws Exception {List<T> datas = (List<T>) params.get("datas");// "template/excel/template.xls"String template = (String) params.get("template"); int templateDataRowIndex = (int) params.get("templateDataRowIndex");String fileType = template.substring(template.lastIndexOf(".") + 1);fileType = Objects.equals("xls", fileType) ? fileType : "xlsx";InputStream templateStream = PoiTemplateUtil.class.getClassLoader().getResourceAsStream(template);Workbook workbook = Objects.equals("xls", fileType) ? new HSSFWorkbook(templateStream) : new XSSFWorkbook(templateStream);// 获取模板sheetSheet sheet = workbook.getSheetAt(0);// 获取模板数据行Row templateDataRow = sheet.getRow(templateDataRowIndex);// 将模板数据行样式用CellStyle数组存起来,后面填充数据用到该模板数据样式CellStyle[] cellStyles = new CellStyle[templateDataRow.getLastCellNum()];for (int i = 0; i < cellStyles.length; i++) {cellStyles[i] = templateDataRow.getCell(i).getCellStyle();}// 填充数据for (int i = 0; i < datas.size(); i++) {// 创建数据行-跟随模板Row row = sheet.createRow(i + templateDataRowIndex);T t = datas.get(i);Field[] declaredFields = t.getClass().getDeclaredFields();for (int j = 0; j < cellStyles.length; j++) {// 创建单元格Cell cell = row.createCell(j);// 设置单元格样式cell.setCellStyle(cellStyles[j]);for (Field field : declaredFields) {// 获取被@ExcelAttr注解标注且注解的sort=j的的属性field.setAccessible(true);ExcelAttr anno = field.getAnnotation(ExcelAttr.class);if (anno != null) {int sort = anno.sort();if (sort == j) {// 设置单元格的值cell.setCellValue(field.get(t).toString());}}}}}// 生成excel文件到指定位置workbook.write(outputStream);
}

5. 导入导出工具类全部代码

com.poi.util.PoiTemplateUtil

package com.poi.util;import com.poi.anno.ExcelAttr;
import com.poi.entity.Employee;
import lombok.NonNull;
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;/*** 类描述:模板导出excel工具类* @Author wang_qz* @Date 2021/8/14 10:30* @Version 1.0*/
public class PoiTemplateUtil<T> {/*** 根据模板填充数据生成excel文件* @param params* @param outputStream* @throws Exception*/public void exportExcel(@NonNull Map<String, Object> params, OutputStream outputStream) throws Exception {List<T> datas = (List<T>) params.get("datas");// "template/excel/template.xls"String template = (String) params.get("template"); int templateDataRowIndex = (int) params.get("templateDataRowIndex");String fileType = template.substring(template.lastIndexOf(".") + 1);fileType = Objects.equals("xls", fileType) ? fileType : "xlsx";InputStream templateStream = PoiTemplateUtil.class.getClassLoader().getResourceAsStream(template);Workbook workbook = Objects.equals("xls", fileType) ? new HSSFWorkbook(templateStream) : new XSSFWorkbook(templateStream);// 获取模板sheetSheet sheet = workbook.getSheetAt(0);// 获取模板数据行Row templateDataRow = sheet.getRow(templateDataRowIndex);// 将模板数据行样式用CellStyle数组存起来,后面填充数据用到该模板数据样式CellStyle[] cellStyles = new CellStyle[templateDataRow.getLastCellNum()];for (int i = 0; i < cellStyles.length; i++) {cellStyles[i] = templateDataRow.getCell(i).getCellStyle();}// 填充数据for (int i = 0; i < datas.size(); i++) {// 创建数据行-跟随模板Row row = sheet.createRow(i + templateDataRowIndex);T t = datas.get(i);Field[] declaredFields = t.getClass().getDeclaredFields();for (int j = 0; j < cellStyles.length; j++) {// 创建单元格Cell cell = row.createCell(j);// 设置单元格样式cell.setCellStyle(cellStyles[j]);for (Field field : declaredFields) {// 获取被@ExcelAttr注解标注且注解的sort=j的的属性field.setAccessible(true);ExcelAttr anno = field.getAnnotation(ExcelAttr.class);if (anno != null) {int sort = anno.sort();if (sort == j) {// 设置单元格的值cell.setCellValue(field.get(t).toString());}}}}}// 生成excel文件到指定位置workbook.write(outputStream);}/*** 大数据模板导入* @param params* @param inputStream* @param clazz* @return* @throws Exception*/public List<T> importExcel(Map<String, Object> params, InputStream inputStream,Class<T> clazz) throws Exception {String fileType = (String) params.get("fileType");fileType = Objects.equals("xls", fileType) ? fileType : "xlsx";Workbook workbook = Objects.equals("xls", fileType) ? new HSSFWorkbook(inputStream) : new XSSFWorkbook(inputStream);Sheet sheet = workbook.getSheetAt(0);int lastRowNum = sheet.getLastRowNum();List<T> dataList = new ArrayList<>();for (int i = 1; i <= lastRowNum; i++) {Row row = sheet.getRow(i);short lastCellNum = row.getLastCellNum();// 获取单元格的值并存入objs中Object[] objs = new Object[lastCellNum];for (int j = 0; j < lastCellNum; j++) {Cell cell = row.getCell(j);Object value = getCellValue(cell);objs[j] = value;}T t = clazz.newInstance();// 反射设置属性值Field[] declaredFields = clazz.getDeclaredFields();for (int k = 0; k < declaredFields.length; k++) {Field field = declaredFields[k];field.setAccessible(true);// 获取被自定义注解ExcelAttr标识的字段ExcelAttr anno = field.getAnnotation(ExcelAttr.class);if (anno != null) {// 获取设置的字段排序int sort = anno.sort();if (sort == k) {String fieldType = field.getType().getName();final Object value = objs[k];if (Objects.equals("int", fieldType) || Objects.equals("Integer", fieldType)) {field.setInt(t, Integer.parseInt(value.toString()));} else if (Objects.equals("double", fieldType) || Objects.equals("Double", fieldType)) {field.setDouble(t, Double.parseDouble(value.toString()));} else {field.set(t, value);}}}}dataList.add(t);}return dataList;}/*** 获取单元格的不同数据类型的值* @param cell* @return*/public static Object getCellValue(Cell cell) {Object cellValue = ""; // 每个单元格的值if (cell != null) {// 数据类型判断, 获取不同类型的数据switch (cell.getCellTypeEnum()) {case NUMERIC: // 数字类型// 日期格式的数字if (HSSFDateUtil.isCellDateFormatted(cell)) {// 日期cellValue = cell.getDateCellValue();} else {// 不是日期格式的数字, 返回字符串格式cellValue = cell.getRichStringCellValue().toString();}break;case STRING: // 字符串cellValue = cell.getStringCellValue();break;case BOOLEAN: // 布尔cellValue = cell.getBooleanCellValue();break;case FORMULA: // 公式cellValue = String.valueOf(cell.getCellFormula());break;case BLANK: // 空cellValue = "";break;case ERROR: // 错误cellValue = "非法字符";break;default:cellValue = "未知类型";break;}}return cellValue;}/*** 造测试数据* @return*/public static List<Employee> getDatas() {List<Employee> datas = new ArrayList<>();for (int i = 1; i <= 10; i++) {Employee employee = Employee.builder().id(Integer.valueOf(i)).departid(i * 100).empno("10000" + i).age(20 + i).sex(i % 2 == 0 ? "男" : "女").job(i % 2 == 0 ? "快递员" : "工程师").name("admin" + i).build();datas.add(employee);}return datas;}
}

6. 数据模板

数据模板就是自己设置好样式的excel文件, 放在项目类路径下, 如下所示:

template/excel/template.xls

image-20210917212806515

7. 单元测试

7.1 测试大数据导入

com.test.poi.PoiTemplateTest#testImportExcel

@Test
public void testImportExcel() throws Exception {String filename = "D:\\study\\excel\\employees.xls";String fileType = filename.substring(filename.lastIndexOf(".") + 1);Map<String, Object> params = new HashMap<>();params.put("fileType", fileType);PoiTemplateUtil util = new PoiTemplateUtil();List<Employee> dataList = util.importExcel(params, new FileInputStream(filename), Employee.class);dataList.forEach(System.out::println);
}

通过控制台日志查看导入数据效果

image-20210917213255884

7.2 测试大数据导出

com.test.poi.PoiTemplateTest#testExportExcel

@Test
public void testExportExcel() throws Exception {Map<String, Object> params = new HashMap<>();params.put("template", "template/excel/template.xls");params.put("templateDataRowIndex", 1); // 模板中第二行是带格式的数据行params.put("datas", getDatas());PoiTemplateUtil util = new PoiTemplateUtil();util.exportExcel(params, new FileOutputStream("D:\\study\\excel\\employees.xls"));
}

com.test.poi.PoiTemplateTest#getDatas

// 测试数据
public static List<Employee> getDatas() {List<Employee> datas = new ArrayList<>();for (int i = 1; i <= 10; i++) {Employee employee = Employee.builder().id(Integer.valueOf(i)).departid(i * 100).empno("10000" + i).age(20 + i).sex(i % 2 == 0 ? "男" : "女").job(i % 2 == 0 ? "快递员" : "工程师").name("admin" + i).build();datas.add(employee);}return datas;
}

导出数据效果

image-20210917213132729

7.3 web端测试

7.3.1 编写controller

com.poi.controller.ExcelController

package com.poi.controller;import com.constant.CSISCONSTANT;
import com.exception.MyException;
import com.poi.entity.Employee;
import com.poi.service.ExcelReaderImpl;
import com.poi.util.PoiTemplateUtil;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.*;
import java.net.URLEncoder;
import java.util.*;/*** 类描述:文件上传下载* @Author wang_qz* @Date 2021/8/14 21:07* @Version 1.0*/
@Controller
@RequestMapping("/excel")
public class ExcelController {@RequestMapping("/toExcelPage")public String todownloadPage() {return "excelPage";}@RequestMapping("/downloadExcel")public void downloadExcel(HttpServletResponse response) throws Exception {// 模板文件String template = "template/excel/template.xls";// 文件类型 xls  xlsxString fileType = template.substring(template.lastIndexOf("."));// 设置响应头response.setContentType("application/vnd.ms-excel");response.setCharacterEncoding("utf-8");// 设置防止中文名乱码String filename = URLEncoder.encode("employees信息表", "utf-8");// 文件下载方式(附件下载还是在当前浏览器打开)response.setHeader("Content-disposition", "attachment;filename=" + filename +fileType);// 构建写入到excel文件的数据PoiTemplateUtil util = new PoiTemplateUtil();Map<String, Object> params = new HashMap<>();params.put("template", template);params.put("templateDataRowIndex", 1); // 模板中第二行是带格式的数据行params.put("datas", util.getDatas());util.exportExcel(params, response.getOutputStream());}@PostMapping("/uploadExcel")@ResponseBodypublic String uploadExcel(@RequestParam("file") Part part) throws Exception {// 获取上传的文件流InputStream inputStream = part.getInputStream();// 上传文件名称String filename = part.getSubmittedFileName();// 读取ExcelString fileType = filename.substring(filename.lastIndexOf(".") + 1);Map<String, Object> params = new HashMap<>();params.put("fileType", fileType);PoiTemplateUtil util = new PoiTemplateUtil();List<Employee> dataList = util.importExcel(params, inputStream,Employee.class);dataList.forEach(System.out::println);return "upload successful!";}@RequestMapping("/toExcelPage2")public String todownloadPage2() {return "excelPage2";}
}
7.3.1 编写jsp页面

webapp/WEB-INF/jsp/excelPage.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head><title>测试excel文件下载</title>
</head>
<body>
<h3>点击下面链接, 进行excel文件下载</h3>
<a href="<c:url value='/excel/downloadExcel'/>">Excel文件下载</a>
<hr/>
<hr/>
<h3>点击下面按钮, 进行excel文件上传</h3>
<form action="<c:url value='/excel/uploadExcel'/>" method="post" enctype="multipart/form-data"><input type="file" name="file"/><br/><input type="submit" value="上传Excel"/>
</form>
</body>
</html>

启动tomcat, 访问http://localhost:8080/excel/toExcelPage, 进入测试页面:

image-20210917214557918

7.3.4 测试效果

下载结果

image-20210917213132729

上传效果

image-20210917213255884

相关推荐

数据分流写入Excel

Poi版本升级优化

StringTemplate实现Excel导出

Poi模板技术

SAX方式实现Excel导入

DOM方式实现Excel导入

Poi实现Excel导出

EasyExcel实现Excel文件导入导出

EasyPoi实现excel文件导入导出

个人博客

欢迎各位访问我的个人博客: https://www.crystalblog.xyz/

备用地址: https://wang-qz.gitee.io/crystal-blog/

这篇关于Poi模板技术的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1108868

相关文章

C++中函数模板与类模板的简单使用及区别介绍

《C++中函数模板与类模板的简单使用及区别介绍》这篇文章介绍了C++中的模板机制,包括函数模板和类模板的概念、语法和实际应用,函数模板通过类型参数实现泛型操作,而类模板允许创建可处理多种数据类型的类,... 目录一、函数模板定义语法真实示例二、类模板三、关键区别四、注意事项 ‌在C++中,模板是实现泛型编程

Java利用poi实现word表格转excel

《Java利用poi实现word表格转excel》这篇文章主要为大家详细介绍了Java如何利用poi实现word表格转excel,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 一、每行对象类需要针对不同的表格进行对应的创建。package org.example.wordToEx

使用Apache POI在Java中实现Excel单元格的合并

《使用ApachePOI在Java中实现Excel单元格的合并》在日常工作中,Excel是一个不可或缺的工具,尤其是在处理大量数据时,本文将介绍如何使用ApachePOI库在Java中实现Excel... 目录工具类介绍工具类代码调用示例依赖配置总结在日常工作中,Excel 是一个不可或缺的工http://

Java实战之利用POI生成Excel图表

《Java实战之利用POI生成Excel图表》ApachePOI是Java生态中处理Office文档的核心工具,这篇文章主要为大家详细介绍了如何在Excel中创建折线图,柱状图,饼图等常见图表,需要的... 目录一、环境配置与依赖管理二、数据源准备与工作表构建三、图表生成核心步骤1. 折线图(Line Ch

Java使用POI-TL和JFreeChart动态生成Word报告

《Java使用POI-TL和JFreeChart动态生成Word报告》本文介绍了使用POI-TL和JFreeChart生成包含动态数据和图表的Word报告的方法,并分享了实际开发中的踩坑经验,通过代码... 目录前言一、需求背景二、方案分析三、 POI-TL + JFreeChart 实现3.1 Maven

基于Java实现模板填充Word

《基于Java实现模板填充Word》这篇文章主要为大家详细介绍了如何用Java实现按产品经理提供的Word模板填充数据,并以word或pdf形式导出,有需要的小伙伴可以参考一下... Java实现按模板填充wor编程d本文讲解的需求是:我们需要把数据库中的某些数据按照 产品经理提供的 word模板,把数据

SpringBoot使用Apache POI库读取Excel文件的操作详解

《SpringBoot使用ApachePOI库读取Excel文件的操作详解》在日常开发中,我们经常需要处理Excel文件中的数据,无论是从数据库导入数据、处理数据报表,还是批量生成数据,都可能会遇到... 目录项目背景依赖导入读取Excel模板的实现代码实现代码解析ExcelDemoInfoDTO 数据传输

java poi实现Excel多级表头导出方式(多级表头,复杂表头)

《javapoi实现Excel多级表头导出方式(多级表头,复杂表头)》文章介绍了使用javapoi库实现Excel多级表头导出的方法,通过主代码、合并单元格、设置表头单元格宽度、填充数据、web下载... 目录Java poi实现Excel多级表头导出(多级表头,复杂表头)上代码1.主代码2.合并单元格3.

poj3468(线段树成段更新模板题)

题意:包括两个操作:1、将[a.b]上的数字加上v;2、查询区间[a,b]上的和 下面的介绍是下解题思路: 首先介绍  lazy-tag思想:用一个变量记录每一个线段树节点的变化值,当这部分线段的一致性被破坏我们就将这个变化值传递给子区间,大大增加了线段树的效率。 比如现在需要对[a,b]区间值进行加c操作,那么就从根节点[1,n]开始调用update函数进行操作,如果刚好执行到一个子节点,

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�