文章目录
前言
最近抽了两天时间,把Java实现表格的相关操作进行了封装,本次封装是基于 POI 的二次开发,最终使用只需要调用一个工具类中的方法,就能满足业务中绝大部门的导出需求。
1.1、测试准备
在做测试前,我们需要將【2. 环境准备】中的四个文件拷贝在工程里
1.2.1 案例图片
1.2.2 导出解析为对象(基础)
首先,你需要创建一个与导入表格对应的Java实体对象,并打上对应的Excel解析的导入注解,@ExcelImport注解的value则为表头名称。
@ExcelExport()这个注解是状态行名,不用的话展示的时候就是字段了
1.2.3 动态导出(基础)
这种方式十分灵活,表中的数据,完全自定义设置。
Controller 代码:
@GetMapping("/export")
public void export(HttpServletResponse response) {
// 表头数据
List<Object> head = Arrays.asList("姓名","年龄","性别","头像");
// 用户1数据
List<Object> user1 = new ArrayList<>();
user1.add("诸葛亮");
user1.add(60);
user1.add("男");
user1.add("https://profile.csdnimg.cn/A/7/3/3_sunnyzyq");
// 用户2数据
List<Object> user2 = new ArrayList<>();
user2.add("大乔");
user2.add(28);
user2.add("女");
user2.add("https://profile.csdnimg.cn/6/1/9/0_m0_48717371");
// 将数据汇总
List<List<Object>> sheetDataList = new ArrayList<>();
sheetDataList.add(head);
sheetDataList.add(user1);
sheetDataList.add(user2);
// 导出数据
ExcelUtils.export(response,"用户表", sheetDataList);
}
2. 环境准备
2.1 Maven 依赖
本次工具类的封装主要依赖于阿里巴巴的JSON包,以及表格处理的POI包,所以我们需要导入这两个库的依赖包,另外,我们还需要文件上传的相关包,毕竟我们在浏览器页面,做Excel导入时,是上传的Excel文件。
<!-- 文件上传 -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.5.7</version>
</dependency>
<!-- JSON -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.41</version>
</dependency>
<!-- POI -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.16</version>
</dependency>
2.1 四个工具类
1.ExcelUtils
package cn.com.sinosoft.util.excel;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFDataValidation;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.usermodel.ClientAnchor.AnchorType;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URL;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Map.Entry;
/**
* Excel导入导出工具类
* 原文链接(不定时增加新功能): https://zyqok.blog.csdn.net/article/details/121994504
*
* @author sunnyzyq
* @date 2021/12/17
*/
public class ExcelUtils {
private static final String XLSX = ".xlsx";
private static final String XLS = ".xls";
public static final String ROW_MERGE = "row_merge";
public static final String COLUMN_MERGE = "column_merge";
private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
private static final String ROW_NUM = "rowNum";
private static final String ROW_DATA = "rowData";
private static final String ROW_TIPS = "rowTips";
private static final int CELL_OTHER = 0;
private static final int CELL_ROW_MERGE = 1;
private static final int CELL_COLUMN_MERGE = 2;
private static final int IMG_HEIGHT = 30;
private static final int IMG_WIDTH = 30;
private static final char LEAN_LINE = '/';
private static final int BYTES_DEFAULT_LENGTH = 10240;
private static final NumberFormat NUMBER_FORMAT = NumberFormat.getNumberInstance();
public static <T> List<T> readFile(File file, Class<T> clazz) throws Exception {
JSONArray array = readFile(file);
return getBeanList(array, clazz);
}
public static <T> List<T> readMultipartFile(MultipartFile mFile, Class<T> clazz) throws Exception {
JSONArray array = readMultipartFile(mFile);
return getBeanList(array, clazz);
}
public static JSONArray readFile(File file) throws Exception {
return readExcel(null, file);
}
public static JSONArray readMultipartFile(MultipartFile mFile) throws Exception {
return readExcel(mFile, null);
}
public static Map<String, JSONArray> readFileManySheet(File file) throws Exception {
return readExcelManySheet(null, file);
}
public static Map<String, JSONArray> readFileManySheet(MultipartFile file) throws Exception {
return readExcelManySheet(file, null);
}
private static <T> List<T> getBeanList(JSONArray array, Class<T> clazz) throws Exception {
List<T> list = new ArrayList<>();
Map<Integer, String> uniqueMap = new HashMap<>(16);
for (int i = 0; i < array.size(); i++) {
list.add(getBean(clazz, array.getJSONObject(i), uniqueMap));
}
return list;
}
/**
* 获取每个对象的数据
*/
private static <T> T getBean(Class<T> c, JSONObject obj, Map<Integer, String> uniqueMap) throws Exception {
T t = c.newInstance();
Field[] fields = c.getDeclaredFields();
List<String> errMsgList = new ArrayList<>();
boolean hasRowTipsField = false;
StringBuilder uniqueBuilder = new StringBuilder();
int rowNum = 0;
for (Field field : fields) {
// 行号
if (field.getName().equals(ROW_NUM)) {
rowNum = obj.getInteger(ROW_NUM);
field.setAccessible(true);
field.set(t, rowNum);
continue;
}
// 是否需要设置异常信息
if (field.getName().equals(ROW_TIPS)) {
hasRowTipsField = true;
continue;
}
// 原始数据
if (field.getName().equals(ROW_DATA)) {
field.setAccessible(true);
field.set(t, obj.toString());
continue;
}
// 设置对应属性值
setFieldValue(t, field, obj, uniqueBuilder, errMsgList);
}
// 数据唯一性校验
if (uniqueBuilder.length() > 0) {
if (uniqueMap.containsValue(uniqueBuilder.toString())) {
Set<Integer> rowNumKeys = uniqueMap.keySet();
for (Integer num : rowNumKeys) {
if (uniqueMap.get(num).equals(uniqueBuilder.toString())) {
errMsgList.add(String.format("数据唯一性校验失败,(%s)与第%s行重复)", uniqueBuilder, num));
}
}
} else {
uniqueMap.put(rowNum, uniqueBuilder.toString());
}
}
// 失败处理
if (errMsgList.isEmpty() && !hasRowTipsField) {
return t;
}
StringBuilder sb = new StringBuilder();
int size = errMsgList.size();
for (int i = 0; i < size; i++) {
if (i == size - 1) {
sb.append(errMsgList.get(i));
} else {
sb.append(errMsgList.get(i)).append(";");
}
}
// 设置错误信息
for (Field field : fields) {
if (field.getName().equals(ROW_TIPS)) {
field.setAccessible(true);
field.set(t, sb.toString());
}
}
return t;
}
private static <T> void setFieldValue(T t, Field field, JSONObject obj, StringBuilder uniqueBuilder, List<String> errMsgList) {
// 获取 ExcelImport 注解属性
ExcelImport annotation = field.getAnnotation(ExcelImport.class);
if (annotation == null) {
return;