All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.kangaroohy.plugin.excel.handler.AbstractSheetWriteHandler Maven / Gradle / Ivy

package com.kangaroohy.plugin.excel.handler;

import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
import com.alibaba.excel.write.handler.WriteHandler;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.kangaroohy.plugin.excel.annotation.*;
import com.kangaroohy.plugin.excel.aop.DynamicNameAspect;
import com.kangaroohy.plugin.excel.config.ExcelConfigProperties;
import com.kangaroohy.plugin.excel.converters.LocalDateStringConverter;
import com.kangaroohy.plugin.excel.converters.LocalDateTimeStringConverter;
import com.kangaroohy.plugin.excel.enhance.WriterBuilderEnhancer;
import com.kangaroohy.plugin.excel.head.HeadGenerator;
import com.kangaroohy.plugin.excel.head.HeadMeta;
import com.kangaroohy.plugin.excel.head.I18nHeaderCellWriteHandler;
import com.kangaroohy.plugin.excel.kit.ExcelException;
import com.kangaroohy.plugin.excel.vo.ExcelExportEnhancer;
import com.kangaroohy.plugin.excel.vo.ExcelSelectorResolver;
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;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.MediaTypeFactory;
import org.springframework.util.Assert;
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.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * @author hy
 * @date 2020/3/31
 */
@RequiredArgsConstructor
public abstract class AbstractSheetWriteHandler implements SheetWriteHandler, ApplicationContextAware {

    private final ExcelConfigProperties configProperties;

    private final ObjectProvider>> converterProvider;

    private final WriterBuilderEnhancer excelWriterBuilderEnhance;

    private ApplicationContext applicationContext;

    @Getter
    @Setter
    @Autowired(required = false)
    private I18nHeaderCellWriteHandler i18nHeaderCellWriteHandler;

    @Override
    public void check(ResponseExcel responseExcel) {
        if (responseExcel.sheets().length == 0) {
            throw new ExcelException("@ResponseExcel sheet 配置不合法");
        }
    }

    @Override
    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) {
            name = UUID.randomUUID().toString();
        }
        String fileName = String.format("%s%s", URLEncoder.encode(name, StandardCharsets.UTF_8), responseExcel.suffix().getValue());
        // 根据实际的文件类型找到对应的 contentType
        String contentType = MediaTypeFactory.getMediaType(fileName)
                .map(MediaType::toString)
                .orElse("application/vnd.ms-excel");
        response.setContentType(contentType);
        response.setCharacterEncoding("utf-8");
        response.addHeader(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, HttpHeaders.CONTENT_DISPOSITION);
        response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename*=" + fileName);
        write(o, response, responseExcel);
    }

    /**
     * 通用的获取ExcelWriter方法
     *
     * @param response      HttpServletResponse
     * @param responseExcel ResponseExcel注解
     * @return ExcelWriter
     */
    @SneakyThrows(IOException.class)
    public ExcelWriter getExcelWriter(HttpServletResponse response, ResponseExcel responseExcel) {
        ExcelWriterBuilder writerBuilder = EasyExcelFactory.write(response.getOutputStream())
                .registerConverter(LocalDateStringConverter.INSTANCE)
                .registerConverter(LocalDateTimeStringConverter.INSTANCE)
                .autoCloseStream(true)
                .excelType(responseExcel.suffix())
                .inMemory(responseExcel.inMemory());

        if (StringUtils.hasText(responseExcel.password())) {
            writerBuilder.password(responseExcel.password());
        }

        if (responseExcel.include().length != 0) {
            writerBuilder.includeColumnFieldNames(Arrays.asList(responseExcel.include()));
        }

        if (responseExcel.exclude().length != 0) {
            writerBuilder.excludeColumnFieldNames(Arrays.asList(responseExcel.exclude()));
        }

        for (Class clazz : responseExcel.writeHandler()) {
            writerBuilder.registerWriteHandler(BeanUtils.instantiateClass(clazz));
        }

        // 开启国际化头信息处理
        if (responseExcel.i18nHeader() && i18nHeaderCellWriteHandler != null) {
            writerBuilder.registerWriteHandler(i18nHeaderCellWriteHandler);
        }

        // 自定义注入的转换器
        registerCustomConverter(writerBuilder);

        for (Class clazz : responseExcel.converter()) {
            writerBuilder.registerConverter(BeanUtils.instantiateClass(clazz));
        }

        String templatePath = configProperties.getTemplatePath();
        if (StringUtils.hasText(responseExcel.template())) {
            ClassPathResource classPathResource = new ClassPathResource(
                    templatePath + File.separator + responseExcel.template());
            InputStream inputStream = classPathResource.getInputStream();
            writerBuilder.withTemplate(inputStream);
        }

        writerBuilder = excelWriterBuilderEnhance.enhanceExcel(writerBuilder, response, responseExcel, templatePath);

        return writerBuilder.build();
    }

    /**
     * 自定义注入转换器 如果有需要,子类自己重写
     *
     * @param builder ExcelWriterBuilder
     */
    public void registerCustomConverter(ExcelWriterBuilder builder) {
        converterProvider.ifAvailable(converters -> converters.forEach(builder::registerConverter));
    }

    /**
     * 获取 WriteSheet 对象
     *
     * @param sheet                 sheet annotation info
     * @param dataClass             数据类型
     * @param template              模板
     * @param bookHeadEnhancerClass 自定义头处理器
     * @return WriteSheet
     */
    public WriteSheet sheet(Sheet sheet, Class dataClass, String template,
                            Class bookHeadEnhancerClass) {

        // Sheet 编号和名称
        Integer sheetNo = sheet.sheetNo() >= 0 ? sheet.sheetNo() : null;
        String sheetName = sheet.sheetName();

        // 是否模板写入
        ExcelWriterSheetBuilder writerSheetBuilder = StringUtils.hasText(template) ? EasyExcelFactory.writerSheet(sheetNo)
                : EasyExcelFactory.writerSheet(sheetNo, sheetName);

        // 头信息增强 1. 优先使用 sheet 指定的头信息增强 2. 其次使用 @ResponseExcel 中定义的全局头信息增强
        Class headGenerateClass = null;
        if (isNotInterface(sheet.headGenerateClass())) {
            headGenerateClass = sheet.headGenerateClass();
        } else if (isNotInterface(bookHeadEnhancerClass)) {
            headGenerateClass = bookHeadEnhancerClass;
        }
        // 定义头信息增强则使用其生成头信息,否则使用 dataClass 来自动获取
        if (headGenerateClass != null) {
            fillCustomHeadInfo(dataClass, bookHeadEnhancerClass, writerSheetBuilder);
        } else if (dataClass != null) {
            writerSheetBuilder.registerWriteHandler(new ExcelDataWriteHandler(getExcelExportEnhancer(dataClass)));

            writerSheetBuilder.head(dataClass);
            if (sheet.excludes().length > 0) {
                writerSheetBuilder.excludeColumnFieldNames(Arrays.asList(sheet.excludes()));
            }
            if (sheet.includes().length > 0) {
                writerSheetBuilder.includeColumnFieldNames(Arrays.asList(sheet.includes()));
            }
        }

        // sheetBuilder 增强
        writerSheetBuilder = excelWriterBuilderEnhance.enhanceSheet(writerSheetBuilder, sheetNo, sheetName, dataClass,
                template, headGenerateClass);

        return writerSheetBuilder.build();
    }

    private void fillCustomHeadInfo(Class dataClass, Class headEnhancerClass,
                                    ExcelWriterSheetBuilder writerSheetBuilder) {
        HeadGenerator headGenerator = this.applicationContext.getBean(headEnhancerClass);
        Assert.notNull(headGenerator, "The header generated bean does not exist.");
        HeadMeta head = headGenerator.head(dataClass);
        writerSheetBuilder.head(head.getHead());
        writerSheetBuilder.excludeColumnFieldNames(head.getIgnoreHeadFields());
    }

    /**
     * 是否为Null Head Generator
     *
     * @param headGeneratorClass 头生成器类型
     * @return true 已指定 false 未指定(默认值)
     */
    private boolean isNotInterface(Class headGeneratorClass) {
        return !Modifier.isInterface(headGeneratorClass.getModifiers());
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    /**
     * 批注、头列颜色设置、下拉框增强
     *
     * @param clazz 类 class
     * @return
     */
    private ExcelExportEnhancer getExcelExportEnhancer(Class clazz) {
        Map requiredMap = new HashMap<>();
        Map notationMap = new HashMap<>();
        List notationList = new ArrayList<>();
        Map selectedMap = new HashMap<>();
        Field[] fields = clazz.getDeclaredFields();
        int index = -1;
        for (Field field : fields) {
            if (field.isAnnotationPresent(ExcelProperty.class)) {
                index++;

                if (!field.isAnnotationPresent(ExcelRequired.class) && !field.isAnnotationPresent(ExcelNotation.class) && !field.isAnnotationPresent(ExcelSelector.class)) {
                    continue;
                }
                ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
                int indexed = excelProperty.index();
                if (indexed == -1) {
                    indexed = index;
                }
                ExcelRequired excelRequired = field.getAnnotation(ExcelRequired.class);
                if (excelRequired != null) {
                    requiredMap.put(indexed, excelRequired.frontColor().getIndex());
                }
                ExcelNotation excelNotation = field.getAnnotation(ExcelNotation.class);
                if (excelNotation != null) {
                    if (excelProperty.value().length <= 1) {
                        notationMap.put(indexed, excelNotation.value());
                    } else {
                        notationList.add(excelNotation);
                    }
                }
                ExcelSelector excelSelector = field.getAnnotation(ExcelSelector.class);
                if (excelSelector != null) {
                    ExcelSelectorResolver resolve = new ExcelSelectorResolver(excelSelector, applicationContext);
                    if (resolve.getSelectorData().length > 0) {
                        selectedMap.put(indexed, resolve);
                    }
                }
            }

        }
        return ExcelExportEnhancer.builder()
                .requiredMap(requiredMap)
                .notationMap(notationMap)
                .notationList(notationList)
                .selectedMap(selectedMap)
                .build();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy