refactor(pigx-common-excel): 重构 Excel 模块,引入内置转换器注册机制并优化 Excel 读写流程

This commit is contained in:
PIGCLOUD committed 2026-05-18 15:56:20 +08:00
1 parent 1d9bfd866f
commit 87a1ea1ae0
19 files changed
+226 -174

No files matched your search

@@ -1,6 +1,6 @@
package com.pig4cloud.pigx.common.excel;
import cn.idev.excel.converters.Converter;
import org.apache.fesod.sheet.converters.Converter;
import com.pig4cloud.pigx.common.excel.aop.ResponseExcelReturnValueHandler;
import com.pig4cloud.pigx.common.excel.config.ExcelConfigProperties;
import com.pig4cloud.pigx.common.excel.enhance.DefaultWriterBuilderEnhancer;
@@ -29,6 +29,8 @@ public class ExcelHandlerConfiguration {
private final ObjectProvider<List<Converter<?>>> converterProvider;
private final ObjectProvider<I18nHeaderCellWriteHandler> i18nHeaderProvider;
/**
* ExcelBuild增强
* @return DefaultWriterBuilderEnhancer 默认什么也不做的增强器
@@ -45,7 +47,8 @@ public class ExcelHandlerConfiguration {
@Bean
@ConditionalOnMissingBean
public SingleSheetWriteHandler singleSheetWriteHandler() {
return new SingleSheetWriteHandler(configProperties, converterProvider, writerBuilderEnhancer());
return new SingleSheetWriteHandler(configProperties, converterProvider, writerBuilderEnhancer(),
i18nHeaderProvider);
}
/**
@@ -54,7 +57,8 @@ public class ExcelHandlerConfiguration {
@Bean
@ConditionalOnMissingBean
public ManySheetWriteHandler manySheetWriteHandler() {
return new ManySheetWriteHandler(configProperties, converterProvider, writerBuilderEnhancer());
return new ManySheetWriteHandler(configProperties, converterProvider, writerBuilderEnhancer(),
i18nHeaderProvider);
}
/**
@@ -6,7 +6,7 @@ import com.pig4cloud.pigx.common.excel.aop.ResponseExcelReturnValueHandler;
import com.pig4cloud.pigx.common.excel.config.ExcelConfigProperties;
import com.pig4cloud.pigx.common.excel.processor.NameProcessor;
import com.pig4cloud.pigx.common.excel.processor.NameSpelExpressionProcessor;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
@@ -14,9 +14,9 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import jakarta.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
@@ -27,15 +27,10 @@ import java.util.List;
* 配置初始化
*/
@AutoConfiguration
@RequiredArgsConstructor
@Import(ExcelHandlerConfiguration.class)
@EnableConfigurationProperties(ExcelConfigProperties.class)
public class ResponseExcelAutoConfiguration {
private final RequestMappingHandlerAdapter requestMappingHandlerAdapter;
private final ResponseExcelReturnValueHandler responseExcelReturnValueHandler;
/**
* SPEL 解析处理器
* @return NameProcessor excel名称解析器
@@ -58,30 +53,41 @@ public class ResponseExcelAutoConfiguration {
}
/**
* 追加 Excel返回值处理器 到 springmvc 中
* 注册 Excel 请求参数解析器。
*/
@PostConstruct
public void setReturnValueHandlers() {
List<HandlerMethodReturnValueHandler> returnValueHandlers = requestMappingHandlerAdapter
.getReturnValueHandlers();
@Bean
public WebMvcConfigurer excelWebMvcConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new RequestExcelArgumentResolver());
}
};
}
List<HandlerMethodReturnValueHandler> newHandlers = new ArrayList<>();
newHandlers.add(responseExcelReturnValueHandler);
assert returnValueHandlers != null;
newHandlers.addAll(returnValueHandlers);
requestMappingHandlerAdapter.setReturnValueHandlers(newHandlers);
}
/**
* 追加 Excel 请求处理器 到 springmvc 中
*/
@PostConstruct
public void setRequestExcelArgumentResolver() {
List<HandlerMethodArgumentResolver> argumentResolvers = requestMappingHandlerAdapter.getArgumentResolvers();
List<HandlerMethodArgumentResolver> resolverList = new ArrayList<>();
resolverList.add(new RequestExcelArgumentResolver());
resolverList.addAll(argumentResolvers);
requestMappingHandlerAdapter.setArgumentResolvers(resolverList);
/**
* 将 Excel 返回值处理器放到 Spring MVC 处理链最前面,确保 @RestController 的
*
* @param requestMappingHandlerAdapter Spring MVC 方法适配器
* @param responseExcelReturnValueHandler Excel 返回值处理器
* @return 容器初始化完成后的处理器注册回调
* @ResponseBody 处理器不会先消费 @ResponseExcel 返回值。
*/
@Bean
public SmartInitializingSingleton excelReturnValueHandlerInitializer(
RequestMappingHandlerAdapter requestMappingHandlerAdapter,
ResponseExcelReturnValueHandler responseExcelReturnValueHandler) {
return () -> {
List<HandlerMethodReturnValueHandler> returnValueHandlers = requestMappingHandlerAdapter
.getReturnValueHandlers();
if (returnValueHandlers == null || returnValueHandlers.contains(responseExcelReturnValueHandler)) {
return;
}
List<HandlerMethodReturnValueHandler> newHandlers = new ArrayList<>(returnValueHandlers.size() + 1);
newHandlers.add(responseExcelReturnValueHandler);
newHandlers.addAll(returnValueHandlers);
requestMappingHandlerAdapter.setReturnValueHandlers(newHandlers);
};
}
}
@@ -30,7 +30,7 @@ public @interface RequestExcel {
/**
* 是否跳过空行
* @return 默认跳过
* @return 默认不跳过
*/
boolean ignoreEmptyRow() default false;
@@ -1,8 +1,8 @@
package com.pig4cloud.pigx.common.excel.annotation;
import cn.idev.excel.converters.Converter;
import cn.idev.excel.support.ExcelTypeEnum;
import cn.idev.excel.write.handler.WriteHandler;
import org.apache.fesod.sheet.converters.Converter;
import org.apache.fesod.sheet.support.ExcelTypeEnum;
import org.apache.fesod.sheet.write.handler.WriteHandler;
import com.pig4cloud.pigx.common.excel.head.HeadGenerator;
import java.lang.annotation.*;
@@ -1,16 +1,17 @@
package com.pig4cloud.pigx.common.excel.aop;
import cn.idev.excel.EasyExcel;
import org.apache.fesod.sheet.FesodSheet;
import org.apache.fesod.sheet.read.builder.ExcelReaderBuilder;
import com.pig4cloud.pigx.common.excel.annotation.RequestExcel;
import com.pig4cloud.pigx.common.excel.converters.*;
import com.pig4cloud.pigx.common.excel.converters.BuiltinConverters;
import com.pig4cloud.pigx.common.excel.handler.ListAnalysisEventListener;
import jakarta.servlet.http.HttpServletRequest;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType;
import org.springframework.ui.ModelMap;
import org.springframework.util.Assert;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.support.WebDataBinderFactory;
@@ -39,9 +40,8 @@ public class RequestExcelArgumentResolver implements HandlerMethodArgumentResolv
}
@Override
@SneakyThrows(Exception.class)
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer modelAndViewContainer,
NativeWebRequest webRequest, WebDataBinderFactory webDataBinderFactory) {
NativeWebRequest webRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
Class<?> parameterType = parameter.getParameterType();
if (!parameterType.isAssignableFrom(List.class)) {
throw new IllegalArgumentException(
@@ -50,17 +50,17 @@ public class RequestExcelArgumentResolver implements HandlerMethodArgumentResolv
// 处理自定义 readListener
RequestExcel requestExcel = parameter.getParameterAnnotation(RequestExcel.class);
assert requestExcel != null;
Assert.notNull(requestExcel, "@RequestExcel annotation must not be null");
Class<? extends ListAnalysisEventListener<?>> readListenerClass = requestExcel.readListener();
ListAnalysisEventListener<?> readListener = BeanUtils.instantiateClass(readListenerClass);
// 获取请求文件流
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
assert request != null;
Assert.notNull(request, "HttpServletRequest must not be null");
InputStream inputStream;
if (request instanceof MultipartRequest) {
MultipartFile file = ((MultipartRequest) request).getFile(requestExcel.fileName());
assert file != null;
if (request instanceof MultipartRequest multipartRequest) {
MultipartFile file = multipartRequest.getFile(requestExcel.fileName());
Assert.notNull(file, "Multipart file [" + requestExcel.fileName() + "] must not be null");
inputStream = file.getInputStream();
}
else {
@@ -71,13 +71,9 @@ public class RequestExcelArgumentResolver implements HandlerMethodArgumentResolv
Class<?> excelModelClass = ResolvableType.forMethodParameter(parameter).getGeneric(0).resolve();
// 这里需要指定读用哪个 class 去读,然后读取第一个 sheet 文件流会自动关闭
EasyExcel.read(inputStream, excelModelClass, readListener)
.registerConverter(LocalDateStringConverter.INSTANCE)
.registerConverter(LocalTimeStringConverter.INSTANCE)
.registerConverter(LocalDateTimeStringConverter.INSTANCE)
.registerConverter(LongStringConverter.INSTANCE)
.registerConverter(StringArrayConverter.INSTANCE)
.ignoreEmptyRow(requestExcel.ignoreEmptyRow())
ExcelReaderBuilder readerBuilder = FesodSheet.read(inputStream, excelModelClass, readListener);
BuiltinConverters.registerTo(readerBuilder);
readerBuilder.ignoreEmptyRow(requestExcel.ignoreEmptyRow())
.sheet()
.headRowNumber(requestExcel.headRowNumber())
.doRead();
@@ -0,0 +1,44 @@
package com.pig4cloud.pigx.common.excel.converters;
import org.apache.fesod.sheet.converters.Converter;
import org.apache.fesod.sheet.read.builder.ExcelReaderBuilder;
import org.apache.fesod.sheet.write.builder.ExcelWriterBuilder;
import java.util.List;
/**
* 内置 Converter 集合,集中管理避免读写两侧重复注册。
*
* @author lengleng
*/
public final class BuiltinConverters {
private BuiltinConverters() {
}
/**
* 读写 Excel 时默认启用的转换器列表。
*/
private static final List<Converter<?>> INSTANCES = List.of(LocalDateStringConverter.INSTANCE,
LocalTimeStringConverter.INSTANCE, LocalDateTimeStringConverter.INSTANCE, LongStringConverter.INSTANCE,
StringArrayConverter.INSTANCE);
/**
* 注册内置转换器到 Excel 写构造器。
*
* @param builder Excel 写构造器
*/
public static void registerTo(ExcelWriterBuilder builder) {
INSTANCES.forEach(builder::registerConverter);
}
/**
* 注册内置转换器到 Excel 读构造器。
*
* @param builder Excel 读构造器
*/
public static void registerTo(ExcelReaderBuilder builder) {
INSTANCES.forEach(builder::registerConverter);
}
}
@@ -1,11 +1,11 @@
package com.pig4cloud.pigx.common.excel.converters;
import cn.idev.excel.converters.Converter;
import cn.idev.excel.enums.CellDataTypeEnum;
import cn.idev.excel.metadata.GlobalConfiguration;
import cn.idev.excel.metadata.data.ReadCellData;
import cn.idev.excel.metadata.data.WriteCellData;
import cn.idev.excel.metadata.property.ExcelContentProperty;
import org.apache.fesod.sheet.converters.Converter;
import org.apache.fesod.sheet.enums.CellDataTypeEnum;
import org.apache.fesod.sheet.metadata.GlobalConfiguration;
import org.apache.fesod.sheet.metadata.data.ReadCellData;
import org.apache.fesod.sheet.metadata.data.WriteCellData;
import org.apache.fesod.sheet.metadata.property.ExcelContentProperty;
import java.text.ParseException;
import java.time.LocalDate;
@@ -1,12 +1,12 @@
package com.pig4cloud.pigx.common.excel.converters;
import cn.idev.excel.converters.Converter;
import cn.idev.excel.enums.CellDataTypeEnum;
import cn.idev.excel.metadata.GlobalConfiguration;
import cn.idev.excel.metadata.data.ReadCellData;
import cn.idev.excel.metadata.data.WriteCellData;
import cn.idev.excel.metadata.property.ExcelContentProperty;
import cn.idev.excel.util.DateUtils;
import org.apache.fesod.sheet.converters.Converter;
import org.apache.fesod.sheet.enums.CellDataTypeEnum;
import org.apache.fesod.sheet.metadata.GlobalConfiguration;
import org.apache.fesod.sheet.metadata.data.ReadCellData;
import org.apache.fesod.sheet.metadata.data.WriteCellData;
import org.apache.fesod.sheet.metadata.property.ExcelContentProperty;
import org.apache.fesod.sheet.util.DateUtils;
import java.text.ParseException;
import java.time.LocalDateTime;
@@ -1,11 +1,11 @@
package com.pig4cloud.pigx.common.excel.converters;
import cn.idev.excel.converters.Converter;
import cn.idev.excel.enums.CellDataTypeEnum;
import cn.idev.excel.metadata.GlobalConfiguration;
import cn.idev.excel.metadata.data.ReadCellData;
import cn.idev.excel.metadata.data.WriteCellData;
import cn.idev.excel.metadata.property.ExcelContentProperty;
import org.apache.fesod.sheet.converters.Converter;
import org.apache.fesod.sheet.enums.CellDataTypeEnum;
import org.apache.fesod.sheet.metadata.GlobalConfiguration;
import org.apache.fesod.sheet.metadata.data.ReadCellData;
import org.apache.fesod.sheet.metadata.data.WriteCellData;
import org.apache.fesod.sheet.metadata.property.ExcelContentProperty;
import java.text.ParseException;
import java.time.LocalTime;
@@ -1,11 +1,11 @@
package com.pig4cloud.pigx.common.excel.converters;
import cn.idev.excel.converters.Converter;
import cn.idev.excel.enums.CellDataTypeEnum;
import cn.idev.excel.metadata.GlobalConfiguration;
import cn.idev.excel.metadata.data.ReadCellData;
import cn.idev.excel.metadata.data.WriteCellData;
import cn.idev.excel.metadata.property.ExcelContentProperty;
import org.apache.fesod.sheet.converters.Converter;
import org.apache.fesod.sheet.enums.CellDataTypeEnum;
import org.apache.fesod.sheet.metadata.GlobalConfiguration;
import org.apache.fesod.sheet.metadata.data.ReadCellData;
import org.apache.fesod.sheet.metadata.data.WriteCellData;
import org.apache.fesod.sheet.metadata.property.ExcelContentProperty;
import java.text.ParseException;
@@ -1,18 +1,15 @@
package com.pig4cloud.pigx.common.excel.converters;
import cn.idev.excel.converters.Converter;
import cn.idev.excel.enums.CellDataTypeEnum;
import cn.idev.excel.metadata.GlobalConfiguration;
import cn.idev.excel.metadata.data.ReadCellData;
import cn.idev.excel.metadata.data.WriteCellData;
import cn.idev.excel.metadata.property.ExcelContentProperty;
import java.text.ParseException;
import java.util.Arrays;
import java.util.stream.Collectors;
import cn.hutool.core.util.StrUtil;
import org.apache.fesod.sheet.converters.Converter;
import org.apache.fesod.sheet.enums.CellDataTypeEnum;
import org.apache.fesod.sheet.metadata.GlobalConfiguration;
import org.apache.fesod.sheet.metadata.data.ReadCellData;
import org.apache.fesod.sheet.metadata.data.WriteCellData;
import org.apache.fesod.sheet.metadata.property.ExcelContentProperty;
/**
* LocalDate and string converter
* String[] and string converter
*
* @author L.cm
*/
@@ -24,7 +21,7 @@ public enum StringArrayConverter implements Converter<String[]> {
INSTANCE;
@Override
public Class supportJavaTypeKey() {
public Class<?> supportJavaTypeKey() {
return String[].class;
}
@@ -34,15 +31,15 @@ public enum StringArrayConverter implements Converter<String[]> {
}
@Override
public String[] convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) throws ParseException {
return cellData.getStringValue().split(",");
public String[] convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) {
return StrUtil.splitToArray(cellData.getStringValue(), StrUtil.COMMA);
}
@Override
public WriteCellData<String> convertToExcelData(String[] value, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) {
return new WriteCellData<>(Arrays.stream(value).collect(Collectors.joining()));
return new WriteCellData<>(String.join(StrUtil.COMMA, value));
}
}
@@ -1,7 +1,7 @@
package com.pig4cloud.pigx.common.excel.enhance;
import cn.idev.excel.write.builder.ExcelWriterBuilder;
import cn.idev.excel.write.builder.ExcelWriterSheetBuilder;
import org.apache.fesod.sheet.write.builder.ExcelWriterBuilder;
import org.apache.fesod.sheet.write.builder.ExcelWriterSheetBuilder;
import com.pig4cloud.pigx.common.excel.annotation.ResponseExcel;
import com.pig4cloud.pigx.common.excel.head.HeadGenerator;
import jakarta.servlet.http.HttpServletResponse;
@@ -1,7 +1,7 @@
package com.pig4cloud.pigx.common.excel.enhance;
import cn.idev.excel.write.builder.ExcelWriterBuilder;
import cn.idev.excel.write.builder.ExcelWriterSheetBuilder;
import org.apache.fesod.sheet.write.builder.ExcelWriterBuilder;
import org.apache.fesod.sheet.write.builder.ExcelWriterSheetBuilder;
import com.pig4cloud.pigx.common.excel.annotation.ResponseExcel;
import com.pig4cloud.pigx.common.excel.head.HeadGenerator;
import jakarta.servlet.http.HttpServletResponse;
@@ -1,31 +1,28 @@
package com.pig4cloud.pigx.common.excel.handler;
import cn.idev.excel.EasyExcel;
import cn.idev.excel.ExcelWriter;
import cn.idev.excel.converters.Converter;
import cn.idev.excel.write.builder.ExcelWriterBuilder;
import cn.idev.excel.write.builder.ExcelWriterSheetBuilder;
import cn.idev.excel.write.handler.WriteHandler;
import cn.idev.excel.write.metadata.WriteSheet;
import cn.hutool.core.util.StrUtil;
import org.apache.fesod.sheet.FesodSheet;
import org.apache.fesod.sheet.ExcelWriter;
import org.apache.fesod.sheet.converters.Converter;
import org.apache.fesod.sheet.write.builder.ExcelWriterBuilder;
import org.apache.fesod.sheet.write.builder.ExcelWriterSheetBuilder;
import org.apache.fesod.sheet.write.handler.WriteHandler;
import org.apache.fesod.sheet.write.metadata.WriteSheet;
import com.pig4cloud.pigx.common.excel.annotation.ResponseExcel;
import com.pig4cloud.pigx.common.excel.annotation.Sheet;
import com.pig4cloud.pigx.common.excel.aop.DynamicNameAspect;
import com.pig4cloud.pigx.common.excel.config.ExcelConfigProperties;
import com.pig4cloud.pigx.common.excel.converters.*;
import com.pig4cloud.pigx.common.excel.converters.BuiltinConverters;
import com.pig4cloud.pigx.common.excel.enhance.WriterBuilderEnhancer;
import com.pig4cloud.pigx.common.excel.head.HeadGenerator;
import com.pig4cloud.pigx.common.excel.head.HeadMeta;
import com.pig4cloud.pigx.common.excel.head.I18nHeaderCellWriteHandler;
import com.pig4cloud.pigx.common.excel.kit.ExcelException;
import jakarta.servlet.http.HttpServletResponse;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.SneakyThrows;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.io.ClassPathResource;
@@ -37,15 +34,13 @@ import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Modifier;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
/**
@@ -53,7 +48,6 @@ import java.util.UUID;
* @author L.cm
* @date 2020/3/31
*/
@RequiredArgsConstructor
public abstract class AbstractSheetWriteHandler implements SheetWriteHandler, ApplicationContextAware {
private final ExcelConfigProperties configProperties;
@@ -62,12 +56,18 @@ public abstract class AbstractSheetWriteHandler implements SheetWriteHandler, Ap
private final WriterBuilderEnhancer excelWriterBuilderEnhance;
private final ObjectProvider<I18nHeaderCellWriteHandler> i18nHeaderProvider;
private ApplicationContext applicationContext;
@Getter
@Setter
@Autowired(required = false)
private I18nHeaderCellWriteHandler i18nHeaderCellWriteHandler;
protected AbstractSheetWriteHandler(ExcelConfigProperties configProperties,
ObjectProvider<List<Converter<?>>> converterProvider, WriterBuilderEnhancer excelWriterBuilderEnhance,
ObjectProvider<I18nHeaderCellWriteHandler> i18nHeaderProvider) {
this.configProperties = configProperties;
this.converterProvider = converterProvider;
this.excelWriterBuilderEnhance = excelWriterBuilderEnhance;
this.i18nHeaderProvider = i18nHeaderProvider;
}
@Override
public void check(ResponseExcel responseExcel) {
@@ -77,22 +77,21 @@ public abstract class AbstractSheetWriteHandler implements SheetWriteHandler, Ap
}
@Override
@SneakyThrows(UnsupportedEncodingException.class)
public void export(Object o, HttpServletResponse response, ResponseExcel responseExcel) {
check(responseExcel);
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
String name = (String) Objects.requireNonNull(requestAttributes)
.getAttribute(DynamicNameAspect.EXCEL_NAME_KEY, RequestAttributes.SCOPE_REQUEST);
if (name == null) {
String name = requestAttributes == null ? null
: (String) requestAttributes.getAttribute(DynamicNameAspect.EXCEL_NAME_KEY,
RequestAttributes.SCOPE_REQUEST);
if (!StringUtils.hasText(name)) {
name = UUID.randomUUID().toString();
}
String fileName = String.format("%s%s", URLEncoder.encode(name, "UTF-8"), responseExcel.suffix().getValue());
// 根据实际的文件类型找到对应的 contentType
String fileName = URLEncoder.encode(name, StandardCharsets.UTF_8) + responseExcel.suffix().getValue();
String contentType = MediaTypeFactory.getMediaType(fileName)
.map(MediaType::toString)
.orElse("application/vnd.ms-excel");
response.setContentType(contentType);
response.setCharacterEncoding("utf-8");
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename*=utf-8''" + fileName);
write(o, response, responseExcel);
}
@@ -105,16 +104,13 @@ public abstract class AbstractSheetWriteHandler implements SheetWriteHandler, Ap
*/
@SneakyThrows(IOException.class)
public ExcelWriter getExcelWriter(HttpServletResponse response, ResponseExcel responseExcel) {
ExcelWriterBuilder writerBuilder = EasyExcel.write(response.getOutputStream())
.registerConverter(LocalTimeStringConverter.INSTANCE)
.registerConverter(LocalDateStringConverter.INSTANCE)
.registerConverter(LocalDateTimeStringConverter.INSTANCE)
.registerConverter(LongStringConverter.INSTANCE)
.registerConverter(StringArrayConverter.INSTANCE)
ExcelWriterBuilder writerBuilder = FesodSheet.write(response.getOutputStream())
.autoCloseStream(true)
.excelType(responseExcel.suffix())
.inMemory(responseExcel.inMemory());
BuiltinConverters.registerTo(writerBuilder);
if (StringUtils.hasText(responseExcel.password())) {
writerBuilder.password(responseExcel.password());
}
@@ -132,8 +128,8 @@ public abstract class AbstractSheetWriteHandler implements SheetWriteHandler, Ap
}
// 开启国际化头信息处理
if (responseExcel.i18nHeader() && i18nHeaderCellWriteHandler != null) {
writerBuilder.registerWriteHandler(i18nHeaderCellWriteHandler);
if (responseExcel.i18nHeader()) {
i18nHeaderProvider.ifAvailable(writerBuilder::registerWriteHandler);
}
// 自定义注入的转换器
@@ -146,7 +142,7 @@ public abstract class AbstractSheetWriteHandler implements SheetWriteHandler, Ap
String templatePath = configProperties.getTemplatePath();
if (StringUtils.hasText(responseExcel.template())) {
ClassPathResource classPathResource = new ClassPathResource(
templatePath + File.separator + responseExcel.template());
templatePath + StrUtil.SLASH + responseExcel.template());
InputStream inputStream = classPathResource.getInputStream();
writerBuilder.withTemplate(inputStream);
}
@@ -180,8 +176,8 @@ public abstract class AbstractSheetWriteHandler implements SheetWriteHandler, Ap
String sheetName = sheet.sheetName();
// 是否模板写入
ExcelWriterSheetBuilder writerSheetBuilder = StringUtils.hasText(template) ? EasyExcel.writerSheet(sheetNo)
: EasyExcel.writerSheet(sheetNo, sheetName);
ExcelWriterSheetBuilder writerSheetBuilder = StringUtils.hasText(template) ? FesodSheet.writerSheet(sheetNo)
: FesodSheet.writerSheet(sheetNo, sheetName);
// 头信息增强 1. 优先使用 sheet 指定的头信息增强 2. 其次使用 @ResponseExcel 中定义的全局头信息增强
Class<? extends HeadGenerator> headGenerateClass = null;
@@ -201,7 +197,7 @@ public abstract class AbstractSheetWriteHandler implements SheetWriteHandler, Ap
writerSheetBuilder.excludeColumnFieldNames(Arrays.asList(sheet.excludes()));
}
if (sheet.includes().length > 0) {
writerSheetBuilder.excludeColumnFieldNames(Arrays.asList(sheet.includes()));
writerSheetBuilder.includeColumnFieldNames(Arrays.asList(sheet.includes()));
}
}
@@ -1,6 +1,7 @@
package com.pig4cloud.pigx.common.excel.handler;
import cn.idev.excel.context.AnalysisContext;
import cn.hutool.core.util.ReflectUtil;
import org.apache.fesod.sheet.context.AnalysisContext;
import com.pig4cloud.pigx.common.excel.annotation.ExcelLine;
import com.pig4cloud.pigx.common.excel.kit.Validators;
import com.pig4cloud.pigx.common.excel.vo.ErrorMessage;
@@ -9,8 +10,10 @@ import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/**
@@ -23,6 +26,11 @@ import java.util.stream.Collectors;
@Slf4j
public class DefaultAnalysisEventListener extends ListAnalysisEventListener<Object> {
/**
* 缓存带 @ExcelLine 标记的 Long 类型行号字段,避免逐行读取时重复反射扫描。
*/
private static final ConcurrentHashMap<Class<?>, Field[]> LINE_FIELDS_CACHE = new ConcurrentHashMap<>();
private final List<Object> list = new ArrayList<>();
private final List<ErrorMessage> errorMessageList = new ArrayList<>();
@@ -39,22 +47,13 @@ public class DefaultAnalysisEventListener extends ListAnalysisEventListener<Obje
.map(ConstraintViolation::getMessage)
.collect(Collectors.toSet());
errorMessageList.add(new ErrorMessage(lineNum, messageSet));
}
else {
Field[] fields = o.getClass().getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(ExcelLine.class) && field.getType() == Long.class) {
try {
field.setAccessible(true);
field.set(o, lineNum);
}
catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
list.add(o);
}
return;
}
for (Field field : resolveLineFields(o.getClass())) {
ReflectUtil.setFieldValue(o, field, lineNum);
}
list.add(o);
}
@Override
@@ -70,6 +69,12 @@ public class DefaultAnalysisEventListener extends ListAnalysisEventListener<Obje
@Override
public List<ErrorMessage> getErrors() {
return errorMessageList;
}
private static Field[] resolveLineFields(Class<?> clazz) {
return LINE_FIELDS_CACHE.computeIfAbsent(clazz, c -> Arrays.stream(c.getDeclaredFields())
.filter(f -> f.isAnnotationPresent(ExcelLine.class) && f.getType() == Long.class)
.toArray(Field[]::new));
}
}
@@ -1,6 +1,6 @@
package com.pig4cloud.pigx.common.excel.handler;
import cn.idev.excel.event.AnalysisEventListener;
import org.apache.fesod.sheet.event.AnalysisEventListener;
import com.pig4cloud.pigx.common.excel.vo.ErrorMessage;
import java.util.List;
@@ -1,13 +1,14 @@
package com.pig4cloud.pigx.common.excel.handler;
import cn.idev.excel.EasyExcel;
import cn.idev.excel.ExcelWriter;
import cn.idev.excel.converters.Converter;
import cn.idev.excel.write.metadata.WriteSheet;
import org.apache.fesod.sheet.FesodSheet;
import org.apache.fesod.sheet.ExcelWriter;
import org.apache.fesod.sheet.converters.Converter;
import org.apache.fesod.sheet.write.metadata.WriteSheet;
import com.pig4cloud.pigx.common.excel.annotation.ResponseExcel;
import com.pig4cloud.pigx.common.excel.annotation.Sheet;
import com.pig4cloud.pigx.common.excel.config.ExcelConfigProperties;
import com.pig4cloud.pigx.common.excel.enhance.WriterBuilderEnhancer;
import com.pig4cloud.pigx.common.excel.head.I18nHeaderCellWriteHandler;
import com.pig4cloud.pigx.common.excel.kit.ExcelException;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.ObjectProvider;
@@ -23,8 +24,9 @@ import java.util.List;
public class ManySheetWriteHandler extends AbstractSheetWriteHandler {
public ManySheetWriteHandler(ExcelConfigProperties configProperties,
ObjectProvider<List<Converter<?>>> converterProvider, WriterBuilderEnhancer excelWriterBuilderEnhance) {
super(configProperties, converterProvider, excelWriterBuilderEnhance);
ObjectProvider<List<Converter<?>>> converterProvider, WriterBuilderEnhancer excelWriterBuilderEnhance,
ObjectProvider<I18nHeaderCellWriteHandler> i18nHeaderProvider) {
super(configProperties, converterProvider, excelWriterBuilderEnhance, i18nHeaderProvider);
}
/**
@@ -54,7 +56,7 @@ public class ManySheetWriteHandler extends AbstractSheetWriteHandler {
List<?> eleList = (List<?>) objList.get(i);
if (CollectionUtils.isEmpty(eleList)) {
sheet = EasyExcel.writerSheet(responseExcel.sheets()[i].sheetName()).build();
sheet = FesodSheet.writerSheet(responseExcel.sheets()[i].sheetName()).build();
}
else {
// 有模板则不指定sheet名
@@ -1,12 +1,13 @@
package com.pig4cloud.pigx.common.excel.handler;
import cn.idev.excel.EasyExcel;
import cn.idev.excel.ExcelWriter;
import cn.idev.excel.converters.Converter;
import cn.idev.excel.write.metadata.WriteSheet;
import org.apache.fesod.sheet.FesodSheet;
import org.apache.fesod.sheet.ExcelWriter;
import org.apache.fesod.sheet.converters.Converter;
import org.apache.fesod.sheet.write.metadata.WriteSheet;
import com.pig4cloud.pigx.common.excel.annotation.ResponseExcel;
import com.pig4cloud.pigx.common.excel.config.ExcelConfigProperties;
import com.pig4cloud.pigx.common.excel.enhance.WriterBuilderEnhancer;
import com.pig4cloud.pigx.common.excel.head.I18nHeaderCellWriteHandler;
import com.pig4cloud.pigx.common.excel.kit.ExcelException;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.ObjectProvider;
@@ -24,8 +25,9 @@ import java.util.List;
public class SingleSheetWriteHandler extends AbstractSheetWriteHandler {
public SingleSheetWriteHandler(ExcelConfigProperties configProperties,
ObjectProvider<List<Converter<?>>> converterProvider, WriterBuilderEnhancer excelWriterBuilderEnhance) {
super(configProperties, converterProvider, excelWriterBuilderEnhance);
ObjectProvider<List<Converter<?>>> converterProvider, WriterBuilderEnhancer excelWriterBuilderEnhance,
ObjectProvider<I18nHeaderCellWriteHandler> i18nHeaderProvider) {
super(configProperties, converterProvider, excelWriterBuilderEnhance, i18nHeaderProvider);
}
/**
@@ -51,7 +53,7 @@ public class SingleSheetWriteHandler extends AbstractSheetWriteHandler {
WriteSheet sheet;
if (CollectionUtils.isEmpty(eleList)) {
sheet = EasyExcel.writerSheet(responseExcel.sheets()[0].sheetName()).build();
sheet = FesodSheet.writerSheet(responseExcel.sheets()[0].sheetName()).build();
}
else {
// 有模板则不指定sheet名
@@ -1,9 +1,9 @@
package com.pig4cloud.pigx.common.excel.head;
import cn.idev.excel.metadata.Head;
import cn.idev.excel.write.handler.CellWriteHandler;
import cn.idev.excel.write.metadata.holder.WriteSheetHolder;
import cn.idev.excel.write.metadata.holder.WriteTableHolder;
import org.apache.fesod.sheet.metadata.Head;
import org.apache.fesod.sheet.write.handler.CellWriteHandler;
import org.apache.fesod.sheet.write.metadata.holder.WriteSheetHolder;
import org.apache.fesod.sheet.write.metadata.holder.WriteTableHolder;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.poi.ss.usermodel.Row;
@@ -51,7 +51,7 @@ public class I18nHeaderCellWriteHandler implements CellWriteHandler {
// 国际化处理
List<String> i18nHeadNames = originHeadNameList.stream()
.map(headName -> propertyPlaceholderHelper.replacePlaceholders(headName, placeholderResolver))
.toList();
.toList();
head.setHeadNameList(i18nHeadNames);
}
}