前提:maven构建
poi针对excel的不同版本需要不同的处理,即.xls和.xlsx文件类型在解析时需要不同的处理:
-- 关键代码
private static Sheet getDefaultSheet(MultipartFile file) throws Exception {
String fileName = file.getOriginalFilename();
Sheet defaultSheet = null;
try (InputStream inputStream = file.getInputStream()) {
if (fileName.endsWith(".xls")) {
defaultSheet = new HSSFWorkbook(inputStream).getSheetAt(0);
} else if (fileName.endsWith(".xlsx")) {
defaultSheet = new XSSFWorkbook(inputStream).getSheetAt(0);
} else {
throw new Exception("CHECK_ME: unknown file type! this file is not excel.");
}
} catch (IOException e) {
e.printStackTrace();
logger.error("IOException", e);
}
return defaultSheet;
}
1、pom.xml文件
org.apache.poi
poi
3.17
org.apache.poi
poi-ooxml
3.17
org.apache.poi
poi-ooxml-schemas
3.17
2、创建导入导出工具类(导入导出字段均可灵活配置)
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import javax.validation.constraints.NotNull;
import java.io.IOException;
import java.io.InputStream;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
/**
* @Author: geyingke
* @Date: 2020/5/13
* @Class: ExcelUtil
* @Discription: TODO
**/
public class ExcelUtil {
private final static String GET_ITEM = "get";
private final static String SET_ITEM = "set";
private static Logger logger = LogManager.getLogger(MethodHandles.lookup().lookupClass());
//普通文件导出方法,标题和字段分开处理
public static HSSFWorkbook createExcelFile(String sheetName, String[] titles, Collection contentBeans, List needFields, HSSFWorkbook workbook) {
if (workbook == null) {
workbook = new HSSFWorkbook();
}
//创建sheet
HSSFSheet sheet = workbook.createSheet(sheetName);
sheet.setDefaultColumnWidth((short) 15);
HSSFCellStyle style = workbook.createCellStyle();
style.setFillForegroundColor(HSSFColor.WHITE.index);
//标题行
AtomicReference row = new AtomicReference<>(sheet.createRow(0));
for (int i = 0; i < titles.length; i++) {
HSSFCell titleCell = row.get().createCell(i);
HSSFRichTextString text = new HSSFRichTextString(titles[i]);
titleCell.setCellValue(text);
}
AtomicInteger index = new AtomicInteger(0);
contentBeans.stream().forEach(t -> {
Class tClass = t.getClass();
int rowIndex = index.addAndGet(1);
row.set(sheet.createRow(rowIndex));
Field[] fields = tClass.getDeclaredFields();
AtomicInteger cellIndex = new AtomicInteger(0);
//过滤final字段,并遍历剩下的字段创建表格行
List fieldList = Arrays.stream(fields).filter(f -> {
if (needFields != null) {
if (!Modifier.isFinal(f.getModifiers()) && needFields.contains((String) f.getName())) {
return true;
}
} else {
if (!Modifier.isFinal(f.getModifiers())) {
return true;
}
}
return false;
}).collect(Collectors.toList());
if (needFields != null) {
for (String fieldName : needFields) {
HSSFCell rowCell = row.get().createCell(cellIndex.get());
rowCell.setCellStyle(style);
String getMethodName = generateGetMethodName(fieldName);
try {
Method getMethod = tClass.getMethod(getMethodName, new Class[]{});
Object value = getMethod.invoke(t, new Object[]{});
if (value instanceof BigDecimal) {
double val = ((BigDecimal) value).doubleValue();
rowCell.setCellValue(val);
} else if (value instanceof Integer) {
Integer integer = (Integer) value;
rowCell.setCellValue(Double.valueOf(integer.toString()));
} else if (value instanceof String) {
HSSFRichTextString textString = new HSSFRichTextString((String) value);
rowCell.setCellValue(textString);
} else if (value instanceof Long) {
double val = Double.parseDouble(value.toString());
rowCell.setCellValue(val);
} else {
if (value == null) {
HSSFRichTextString textString = new HSSFRichTextString("");
rowCell.setCellValue(textString);
} else {
logger.info("CHECKME: unknown class type of value." + value.toString());
}
}
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
logger.error("FIXME: unExcepted exception!", e);
} finally {
cellIndex.addAndGet(1);
}
}
} else {
fieldList.forEach(field -> {
HSSFCell rowCell = row.get().createCell(cellIndex.get());
rowCell.setCellStyle(style);
String fieldName = field.getName();
String getMethodName = generateGetMethodName(fieldName);
try {
Method getMethod = tClass.getMethod(getMethodName, new Class[]{});
Object value = getMethod.invoke(t, new Object[]{});
if (value instanceof BigDecimal) {
double val = ((BigDecimal) value).doubleValue();
rowCell.setCellValue(val);
} else if (value instanceof Integer) {
Integer integer = (Integer) value;
rowCell.setCellValue(Double.valueOf(integer.toString()));
} else if (value instanceof String) {
HSSFRichTextString textString = new HSSFRichTextString((String) value);
rowCell.setCellValue(textString);
} else if (value instanceof Long) {
double val = Double.parseDouble(value.toString());
rowCell.setCellValue(val);
} else {
if (value == null) {
HSSFRichTextString textString = new HSSFRichTextString("");
rowCell.setCellValue(textString);
} else {
logger.info("CHECKME: unknown class type of value." + value.toString());
}
}
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
logger.error("FIXME: unExcepted exception!", e);
} finally {
cellIndex.addAndGet(1);
}
});
}
});
return workbook;
}
//配置型excel文件导出方法,fieldMap为配置的标题和字段【格式为:key-标题名称;value-字段。
//字段可以配置多个(当前处理为,按配置顺序取到有值的字段)】
public static HSSFWorkbook createExcelFileUseFieldMap(String sheetName, Collection contentBeans, @NotNull Map> fieldMap, HSSFWorkbook workbook) {
Assert.notNull(fieldMap, "field map can not be null");
if (workbook == null) {
workbook = new HSSFWorkbook();
}
//创建sheet
HSSFSheet sheet = workbook.createSheet(sheetName);
sheet.setDefaultColumnWidth((short) 15);
HSSFCellStyle style = workbook.createCellStyle();
style.setFillForegroundColor(HSSFColor.WHITE.index);
//标题行
AtomicReference row = new AtomicReference<>(sheet.createRow(0));
Object[] titles = fieldMap.keySet().toArray();
for (int i = 0; i < titles.length; i++) {
HSSFCell titleCell = row.get().createCell(i);
HSSFRichTextString text = new HSSFRichTextString(String.valueOf(titles[i]));
titleCell.setCellValue(text);
}
AtomicInteger index = new AtomicInteger(0);
contentBeans.stream().forEach(t -> {
Class tClass = t.getClass();
int rowIndex = index.addAndGet(1);
row.set(sheet.createRow(rowIndex));
Field[] fields = tClass.getDeclaredFields();
AtomicInteger cellIndex = new AtomicInteger(0);
//过滤final字段,并遍历剩下的字段创建表格行
Collection> fieldsCollection = fieldMap.values();
List fieldList = Arrays.stream(fields).filter(f -> {
if (fieldsCollection != null) {
if (!Modifier.isFinal(f.getModifiers())) {
for (List fieldLis : fieldsCollection) {
if (null != fieldLis && fieldLis.contains(String.valueOf(f.getName()))) {
return true;
}
}
return false;
}
} else {
if (!Modifier.isFinal(f.getModifiers())) {
return true;
}
}
return false;
}).collect(Collectors.toList());
if (fieldsCollection != null) {
fieldsCollectionLoop:
for (List fieldNames : fieldsCollection) {
try {
HSSFCell rowCell = row.get().createCell(cellIndex.get());
rowCell.setCellStyle(style);
fieldNameLoop:
for (int i = 0; i < fieldNames.size(); i++) {
String fieldName = fieldNames.get(i);
String getMethodName = generateGetMethodName(fieldName);
Method getMethod = tClass.getMethod(getMethodName, new Class[]{});
Object value = getMethod.invoke(t, new Object[]{});
if (null != value) {
if (value instanceof BigDecimal) {
double val = ((BigDecimal) value).doubleValue();
rowCell.setCellValue(val);
} else if (value instanceof Integer) {
Integer integer = (Integer) value;
rowCell.setCellValue(Double.valueOf(integer.toString()));
} else if (value instanceof String) {
HSSFRichTextString textString = new HSSFRichTextString((String) value);
rowCell.setCellValue(textString);
} else if (value instanceof Long) {
double val = Double.parseDouble(value.toString());
rowCell.setCellValue(val);
} else if (value instanceof Boolean) {
//TODO:布尔类型字段的特殊处理
} else {
logger.info("CHECKME: unknown class type of value." + value.toString());
}
continue fieldsCollectionLoop;
} else {
if (i == fieldNames.size() - 1) {
HSSFRichTextString textString = new HSSFRichTextString("");
rowCell.setCellValue(textString);
}
continue fieldNameLoop;
}
}
} catch (Exception e) {
logger.error("FIXME: unExcepted exception!", e);
} finally {
cellIndex.addAndGet(1);
}
}
} else {
logger.error("CHECK_ME: illegal field collection,please check");
}
});
return workbook;
}
/**
* 导入excel文件,并转换成指定类型的List集合
*
* @param file
* @param fieldMap -- 标题和字段的映射,key为标题名称,value为标题字段
* @param tClass
* @param
* @return
* @throws Exception
*/
public static List commonImportExcel(MultipartFile file, Map fieldMap, Class tClass, int titleRowIndex) throws Exception {
List result = new ArrayList<>();
//获取默认表
Sheet defaultSheet = getDefaultSheet(file);
if (defaultSheet != null) {
if (defaultSheet.getLastRowNum() <= 1) {
throw new Exception("CHECK_ME: file is empty.");
}
//获取标题行,并创建标题的列映射
Map title2ColumnMap = new HashMap<>();
Row titleRow = defaultSheet.getRow(titleRowIndex);
for (int index = 0; ; index++) {
Cell cell = titleRow.getCell(index);
if (null != cell) {
String title = cell.getStringCellValue();
if (StringUtils.isEmpty(title)) {
break;
} else {
String[] strings = fieldMap.keySet().toArray(new String[]{});
for (int idx = 0; idx < strings.length; idx++) {
String key = strings[idx];
if (title.contains(key)) {
title2ColumnMap.put(index, fieldMap.get(key));
break;
} else {
if (idx == strings.length) {
//fieldMap配置有不正确的,终止导入操作
throw new Exception("CHECK_ME: unknown field! please check configuration of field map");
}
}
}
}
} else {
break;
}
}
//开始遍历除标题之外的行
for (Row row : defaultSheet) {
int rowIndex = row.getRowNum();
if (rowIndex < titleRowIndex + 1) {
//越过标题行,及标题行之前的所有内容
continue;
}
T tInstance = tClass.newInstance();
for (Integer keyIndex : title2ColumnMap.keySet()) {
Cell cell = row.getCell(keyIndex);
if (null != cell) {
String cellVal = getStringCellValue(cell);
//越过值为空的字段
if (StringUtils.isEmpty(cellVal)) {
break;
}
String fieldName = title2ColumnMap.get(keyIndex);
Field field = getBeanInnerField(tClass, fieldName);
Class fieldType = field.getType();
Method method = getBeanInnerFieldMethod(tClass, fieldName, fieldType);
setFieldValue(method, tInstance, fieldType, cellVal);
} else {
break;
}
}
result.add(tInstance);
}
}
return result.size() > 0 ? result : null;
}
/**
* 导入excel文件,并转换成指定类型的List集合
*
* @param file
* @param fieldMap -- 标题和字段的映射,key为类中字段 |
* value为标题字段匹配规则集合(支持正则)(可以存在多个)
* @param tClass
* @param
* @return
* @throws Exception
*/
public static List importExcel(MultipartFile file, Map> fieldMap, Class tClass, int titleRowIndex) throws Exception {
List result = new ArrayList<>();
//获取默认表
Sheet defaultSheet = getDefaultSheet(file);
if (defaultSheet != null) {
if (defaultSheet.getLastRowNum() <= 1) {
throw new Exception("CHECK_ME: file is empty.");
}
//获取标题行,并创建标题的列映射
Map> beanField2TitleMap = getBeanField2TitleMap(defaultSheet.getRow(titleRowIndex), fieldMap);
//开始遍历除标题之外的行
for (Row row : defaultSheet) {
int rowIndex = row.getRowNum();
if (rowIndex < titleRowIndex + 1) {
//越过标题行,及标题行之前的所有内容
continue;
}
T tInstance = tClass.newInstance();
for (Map.Entry> entry : beanField2TitleMap.entrySet()) {
final String fieldName = entry.getKey();
final List cellIndexList = entry.getValue();
Field field = getBeanInnerField(tClass, fieldName);
Class fieldType = field.getType();
checkFieldTypeAndSetVal(row, fieldType, cellIndexList, fieldName, tClass, tInstance);
}
result.add(tInstance);
}
}
return result.size() > 0 ? result : null;
}
/**
* 字段的特殊处理,留待后续扩充使用
* @param row
* @param fieldType
* @param cellIndexList
* @param fieldName
* @param tClass
* @param tInstance
* @param
* @throws Exception
*/
private static void checkFieldTypeAndSetVal(Row row, Class fieldType, List cellIndexList, String fieldName, Class tClass, T tInstance) throws Exception {
if (null != cellIndexList && cellIndexList.size() > 0) {
if (fieldType.equals(List.class)) {
List cellValLis = new ArrayList<>();
for (Integer cellIndex : cellIndexList) {
Cell cell = row.getCell(cellIndex);
if (cell != null) {
String cellVal = getStringCellValue(cell);
//越过值为空的字段
if (StringUtils.isEmpty(cellVal)) {
break;
}
cellValLis.add(cellVal);
}
}
Method method = getBeanInnerFieldMethod(tClass, fieldName, fieldType);
setFieldValue(method, tInstance, fieldType, cellValLis);
} else {
for (Integer cellIndex : cellIndexList) {
Cell cell = row.getCell(cellIndex);
if (cell != null) {
String cellVal = getStringCellValue(cell);
//越过值为空的字段
if (!StringUtils.isEmpty(cellVal)) {
Method method = getBeanInnerFieldMethod(tClass, fieldName, fieldType);
setFieldValue(method, tInstance, fieldType, cellVal);
}
}
}
}
}
}
/**
* 获取类中字段与标题的映射关系
* (字段:标题==》1:n)
*
* @param titleRow
* @param fieldMap
* @return
*/
private static Map> getBeanField2TitleMap(Row titleRow, Map> fieldMap) {
Map> beanField2TitleMap = new HashMap<>();
String[] fieldArr = fieldMap.keySet().toArray(new String[]{});
for (String key : fieldArr) {
final List titleList = fieldMap.get(key);
List titleIndexList = new ArrayList<>();
for (String titleName : titleList) {
for (int index = 0; ; index++) {
Cell cell = titleRow.getCell(index);
if (null != cell) {
String title = cell.getStringCellValue();
if (StringUtils.isEmpty(title)) {
break;
} else {
if (title.matches(titleName)) {
titleIndexList.add(index);
}
}
} else {
break;
}
}
}
beanField2TitleMap.put(key, titleIndexList);
}
return beanField2TitleMap;
}
/**
* 获取默认的数据表
*
* @param file
* @return
* @throws Exception
*/
private static Sheet getDefaultSheet(MultipartFile file) throws Exception {
String fileName = file.getOriginalFilename();
Sheet defaultSheet = null;
try (InputStream inputStream = file.getInputStream()) {
if (fileName.endsWith(".xls")) {
defaultSheet = new HSSFWorkbook(inputStream).getSheetAt(0);
} else if (fileName.endsWith(".xlsx")) {
defaultSheet = new XSSFWorkbook(inputStream).getSheetAt(0);
} else {
throw new Exception("CHECK_ME: unknown file type! this file is not excel.");
}
} catch (IOException e) {
e.printStackTrace();
logger.error("IOException", e);
}
return defaultSheet;
}
/**
* 设置类field的值
*
* @param method
* @param tInstance
* @param fieldType
* @param cellVal
* @param
* @throws Exception
*/
private static void setFieldValue(Method method, T tInstance, Class fieldType, Object cellVal) throws Exception {
if (fieldType.equals(String.class)) {
method.invoke(tInstance, cellVal);
} else if (fieldType.equals(int.class) || fieldType.equals(Integer.class)) {
method.invoke(tInstance, new Double((String) cellVal).intValue());
} else if (fieldType.equals(long.class) || fieldType.equals(Long.class)) {
method.invoke(tInstance, new Double((String) cellVal).longValue());
} else if (fieldType.equals(List.class)) {
method.invoke(tInstance, cellVal);
} else if (fieldType.equals(Object.class)) {
method.invoke(tInstance, cellVal);
} else {
throw new Exception("FIX_ME: field type miss match.");
}
}
/**
* 从当前类和父类,获取类中feild的set方法
*
* @param tClass
* @param fieldName
* @param fieldType
* @param
* @return
* @throws NoSuchMethodException
*/
private static Method getBeanInnerFieldMethod(Class tClass, String fieldName, Class fieldType) throws NoSuchMethodException {
Method method = null;
try {
method = tClass.getDeclaredMethod(generateSetMethodName(fieldName), fieldType);
} catch (NoSuchMethodException e) {
method = tClass.getSuperclass().getDeclaredMethod(generateSetMethodName(fieldName), fieldType);
}
return method;
}
/**
* 从当前类和父类,获取类中的feild
*
* @param tClass
* @param fieldName
* @param
* @return
* @throws NoSuchFieldException
*/
private static Field getBeanInnerField(Class tClass, String fieldName) throws NoSuchFieldException {
Field field = null;
try {
field = tClass.getDeclaredField(fieldName);
} catch (NoSuchFieldException e) {
field = tClass.getSuperclass().getDeclaredField(fieldName);
}
return field;
}
private static String getStringCellValue(Cell cell) {
String res = "";
switch (cell.getCellTypeEnum()) {
case _NONE:
res = null;
break;
case STRING:
res += cell.getStringCellValue();
break;
case NUMERIC:
res += cell.getNumericCellValue();
break;
default:
res = null;
break;
}
return res;
}
/**
* 生成get方法名称
*
* @param fieldName
* @return
*/
public static String generateGetMethodName(String fieldName) {
StringBuffer stringBuffer = new StringBuffer(GET_ITEM);
return stringBuffer.append(fieldName.substring(0, 1).toUpperCase()
+ fieldName.substring(1)).toString();
}
/**
* 生成set方法
*
* @param fieldName
* @return
*/
public static String generateSetMethodName(String fieldName) {
StringBuffer stringBuffer = new StringBuffer(SET_ITEM);
return stringBuffer.append(fieldName.substring(0, 1).toUpperCase()
+ fieldName.substring(1)).toString();
}
}
3、导入应用
-- 关键代码
public xxx import(MultipartFile file, @RequestParam String id) throws Exception {
logger.info("request param --> id:" + id);
//标签类型 项目名称 编号 标准问句 答案顺序 答案 答案是否可修改 一级业务分类 二级业务分类 补充说明 链接
//配置标题字段映射
Map> fieldMap = new HashMap>() {{
put("knowledgeProjectName", new ArrayList() {{
add("项目名称");
}});
put("knowledgeCode", new ArrayList() {{
add("编号");
}});
put("question", new ArrayList() {{
add("标准问句");
}});
put("answerOrder", new ArrayList() {{
add("答案顺序");
}});
put("answer", new ArrayList() {{
add("答案");
}});
put("answerStatus", new ArrayList() {{
add("答案是否可修改");
}});
put("serviceTypeName", new ArrayList() {{
add("一级业务分类");
add("二级业务分类");
}});
put("customField", new ArrayList() {{
add("补充说明");
}});
put("linkUrl", new ArrayList() {{
add("链接");
}});
put("similarQuestion", new ArrayList() {{
add("相似问句[0-9]");
}});
}};
//导入excel转换成实体类集合
List beans= ExcelUtil.importExcel(file, fieldMap, Bean.class, 0);
}
4、导出应用
-- 关键代码
String sheetName = "日志" + System.currentTimeMillis();
HSSFWorkbook excelFile = ExcelUtil.createExcelFileUseFieldMap(sheetName, faultLogList, faultLogExportFieldMap, null);
String excelName = String.valueOf(sheetName + ".xls");
Map result = new HashMap<>();
result.put("url", outServer + requestPath + excelName);
FileOutputStream fileOutputStream = new FileOutputStream(path + excelName);
excelFile.write(fileOutputStream);
-- 使用springboot自带tomcat实现文件导出到本地配置目录,并支持远程下载
-- yml文件配置文件输出路径,及外部访问路径
out:
server: 11.33.7.*:6070/*项目路径*/
request-path: /excel/
resource:
path: D:/static/excel/
-- 配置spring web路径映射
@Configuration
public class MyWebConfigurer extends WebMvcConfigurerAdapter{
//WebMvcConfigurerAdapter类当前高版本springboot有些不支持,可继承WebMvcConfigurationSupport实现相同功能
@Value("${out.resource.path}")
String path;
@Value("${out.request-path}")
String requestPath;
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations("classpath:/static");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
registry.addResourceHandler(requestPath + "**").addResourceLocations("file:" + path);
super.addResourceHandlers(registry);
}
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
//取消excel请求路径拦截,不进行权限校验
@Override
public void addInterceptors(InterceptorRegistry registry) {
//设定excel导出请求路径不做拦截
registry.addInterceptor(new PermissionInterceptor()).excludePathPatterns("/excel/**");
}
}