EasyExcel

 EasyExcel

1.pom文件

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>3.3.1</version>
</dependency>

2.引入文件

2.1SheetName

package com.example.demo.utils;

import java.lang.annotation.*;

/**
 * sheet名称
 * @author
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(value={ElementType.TYPE})
@Documented
@Inherited
public @interface SheetName {
    String value() default "";
}

2.2MyBeanCopy

package com.example.demo.utils;

import org.springframework.beans.*;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.util.Assert;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;

public class MyBeanCopy {

    private final class DateToStringConverter implements Converter<Date, String> {
        private DateFormat df;

        private DateToStringConverter(String format) {
            df = new SimpleDateFormat(format);
        }

        @Override
        public String convert(Date source) {

            return df.format(source);
        }
    }

    private static final String DATE_FORMAT = "yyyy-MM-dd";

    private static Object convertForProperty(Wrapper wrapper, Object object, Object value, String propertyName)
            throws TypeMismatchException {
        Object result;
        if (wrapper == null) {
            result = null;
        } else {
            wrapper.setWrappedInstance(object);
            result = wrapper.getBeanWrapper().convertForProperty(value, propertyName);
        }
        return result;
    }

    private static Object copyProperties(Object source, Object target) throws BeansException {
        Wrapper wrapper = new MyBeanCopy().new Wrapper(source);
        copyProperties(wrapper, source, target);
        return target;
    }

    /**
     * Copy the property values of the given source bean into the target bean.
     * <p>
     * Note: The source and target classes do not have to match or even be derived from each other, as long as the properties match. Any bean
     * properties that the source bean exposes but the target bean does not will silently be ignored.
     * <p>
     * This is just a convenience method. For more complex transfer needs, consider using a full BeanWrapper.
     *
     * @param source the source bean
     * @param target the target bean
     * @throws BeansException if the copying failed
     */
    private static void copyProperties(Wrapper wrapper, Object source, Object target) throws BeansException {
        Assert.notNull(source, "Source must not be null");
        Assert.notNull(target, "Target must not be null");

        Class<?> actualEditable = target.getClass();
        PropertyDescriptor[] targetPds = BeanUtils.getPropertyDescriptors(actualEditable);

        for (PropertyDescriptor targetPd : targetPds) {
            if (targetPd.getWriteMethod() != null) {
                PropertyDescriptor sourcePd = BeanUtils.getPropertyDescriptor(source.getClass(), targetPd.getName());
                if (sourcePd != null && sourcePd.getReadMethod() != null) {
                    try {
                        Method readMethod = sourcePd.getReadMethod();
                        if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
                            readMethod.setAccessible(true);
                        }
                        Object value = readMethod.invoke(source);
                        // 判断是否类型不一致
                        if (value != null && !(targetPd.getPropertyType().isInstance(value))) {
                            // 数据转型
                            value = convertForProperty(wrapper, target, value, targetPd.getName());
                        }
                        Method writeMethod = targetPd.getWriteMethod();
                        if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
                            writeMethod.setAccessible(true);
                        }
                        writeMethod.invoke(target, value);
                    } catch (Exception ex) {
                        throw new FatalBeanException("Could not copy properties from source to target", ex);
                    }
                }
            }
        }

    }

    private final class Wrapper {

        private GenericConversionService conversion;
        private BeanWrapperImpl bean;

        private Wrapper(Object object) {
            conversion = initDefaultConversionService();
            bean = initDefaultBeanWrapper(conversion, object);
        }

        private void setWrappedInstance(Object object) {
            bean.setWrappedInstance(object);
        }

        private GenericConversionService initDefaultConversionService() {
            GenericConversionService conversionService = new DefaultConversionService();
            conversionService.addConverter(new DateToStringConverter(DATE_FORMAT));
            return conversionService;
        }

        private BeanWrapperImpl initDefaultBeanWrapper(@SuppressWarnings("hiding") ConversionService conversion,
                                                       Object object) {
            BeanWrapperImpl beanWrapper = new BeanWrapperImpl(object);
            beanWrapper.setConversionService(conversion);
            SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);
            dateFormat.setLenient(false);
            beanWrapper.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
            return beanWrapper;
        }

        private BeanWrapperImpl getBeanWrapper() {
            return bean;
        }
    }

    /**
     * 复制源对象到目的对象
     */
    private static void convert(Object source, Object target) {
        copyProperties(source, target);
    }

    public static <T> List<T> convert(List<?> sources, Class<T> targetClass) {
        List<?> sourcesObj = sources;
        if (sourcesObj == null) {
            sourcesObj = Collections.emptyList();
        }
        List<T> targets = new ArrayList<>(sourcesObj.size());
        MyBeanCopy.convert(sourcesObj, targets, targetClass);
        return targets;
    }

    private static <T> void convert(List<?> sources, List<T> targets, Class<T> targetClass) {
        if (targets == null) {
            return;
        }
        targets.clear();
        if (sources == null) {
            return;
        }
        for (Object obj : sources) {
            try {
                T target = targetClass.newInstance();
                targets.add(target);
                convert(obj, target);
            } catch (Exception e) {
                //do something
                return;
            }
        }
    }
}

2.3ExcelListener

package com.example.demo.utils;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author
 */
public class ExcelListener<T> extends AnalysisEventListener<T> {

    private final List<Object> dataList = new ArrayList<>();

    private static final Integer maxSize = 10000;

    private Class<T> rowModel;

    public ExcelListener(Class<T> rowModel) {
        this.rowModel = rowModel;
    }

    /**
     * 通过 AnalysisContext 对象还可以获取当前 sheet,当前行等数据
     */
    @Override
    public void invoke(T object, AnalysisContext context) {
        if(!checkObjAllFieldsIsNull(object)) {
            dataList.add(object);
        }
        if (dataList.size() > maxSize) {
            throw new RuntimeException("最大只能导入10000行数据");
        }
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        //do something
    }

    private static final String SERIAL_VERSION_UID = "serialVersionUID";

    /**
     * 判断对象中属性值是否全为空
     */
    private static boolean checkObjAllFieldsIsNull(Object object) {
        if (null == object) {
            return true;
        }
        try {
            for (Field f : object.getClass().getDeclaredFields()) {
                f.setAccessible(true);
                //只校验带ExcelProperty注解的属性
                ExcelProperty property = f.getAnnotation(ExcelProperty.class);
                if(property == null || SERIAL_VERSION_UID.equals(f.getName())){
                    continue;
                }
                if (f.get(object) != null && EasyExcelUtil.isNotBlank(f.get(object).toString())) {
                    return false;
                }
            }
        } catch (Exception e) {
            //do something
        }
        return true;
    }

    public List<?> getDataList() {
        return dataList;
    }

    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        Field[] fields = rowModel.getDeclaredFields();
        int length = fields.length;
        List<String> headNameList = new ArrayList<>();
        for (int i = 0; i < length; i++) {
            ExcelProperty annotation = fields[i].getAnnotation(ExcelProperty.class);
            if (annotation != null) {
                headNameList.add(annotation.value()[0]);
            }
        }
        int size = headNameList.size();
        if (headMap.size() != size) {
            throw new RuntimeException("导入模板错误!请下载正确的导入模板");
        }
        for (int i = 0; i < size; i++) {
            if (!headNameList.get(i).equals(headMap.get(i))) {
                throw new RuntimeException("导入模板错误!请下载正确的导入模板");
            }
        }

    }
}

2.4EasyExcelUtil

package com.joyoung.logistics.workbench.basic.util;


import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.handler.WriteHandler;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.joyoung.logistics.workbench.basic.common.exception.AppException;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;

/**
 * ExcelUtil
 * 基于easyExcel的开源框架,poi版本3.17
 * BeanCopy AppException 属于自定义数据,属于可自定义依赖
 * 工具类尽可能还是需要减少对其他java的包的依赖
 *
 * @author
 */
public class EasyExcelUtil {
    /**
     * 私有化构造方法
     */
    private EasyExcelUtil() {
    }

    /**
     * 读取 Excel(多个 sheet)
     */
    public static <T> List<T> readExcel(ExcelReader reader, Class<T> rowModel, int sheetCount) {
        if (reader == null) {
            return new ArrayList<>();
        }
        List<ReadSheet> readSheetList = new ArrayList<>();
        ExcelListener<T> excelListener = new ExcelListener<>(rowModel);
        ReadSheet readSheet = EasyExcel.readSheet(sheetCount)
                .head(rowModel)
                .registerReadListener(excelListener)
                .build();
        readSheetList.add(readSheet);
        reader.read(readSheetList);
        return getExtendsBeanList(excelListener.getDataList(), rowModel);
    }

    /**
     * 读取 Excel(多个 sheet)
     * 将多sheet合并成一个list数据集,通过自定义ExcelReader继承AnalysisEventListener
     * 重写invoke doAfterAllAnalysed方法
     * getExtendsBeanList 主要是做Bean的属性拷贝 ,可以通过ExcelReader中添加的数据集直接获取
     *
     * @param excel    文件
     * @param rowModel 实体类映射
     */
    private static List[] readExcel(MultipartFile excel, Integer sheetNo, Class<?>[] rowModel) {
        ExcelReader reader = getReader(excel);
        if (reader == null) {
            return new ArrayList[rowModel.length];
        }
        List[] result = new ArrayList[rowModel.length];
        for (int sheetCount = 0; sheetCount < rowModel.length; sheetCount++) {
            if (sheetNo != null && sheetNo != sheetCount) {
                continue;
            }
            result[sheetCount].addAll(readExcel(reader, rowModel[sheetCount], sheetCount));
        }
        return result;
    }

    /**
     * 读取 Excel(多个 sheet)
     * 将多sheet合并成一个list数据集,通过自定义ExcelReader继承AnalysisEventListener
     * 重写invoke doAfterAllAnalysed方法
     * getExtendsBeanList 主要是做Bean的属性拷贝 ,可以通过ExcelReader中添加的数据集直接获取
     *
     * @param excel    文件
     * @param rowModel 实体类映射
     */
    public static List[] readExcel(MultipartFile excel, Class<?>... rowModel) {
        ExcelReader reader = getReader(excel);
        if (reader == null) {
            return new ArrayList[rowModel.length];
        }
        List[] result = new ArrayList[rowModel.length];
        for (int sheetCount = 0; sheetCount < rowModel.length; sheetCount++) {
            result[sheetCount] = new ArrayList<>(readExcel(reader, rowModel[sheetCount], sheetCount));
        }
        return result;
    }

    /**
     * 读取 Excel(单个 sheet)
     * 将多sheet合并成一个list数据集,通过自定义ExcelReader继承AnalysisEventListener
     * 重写invoke doAfterAllAnalysed方法
     * getExtendsBeanList 主要是做Bean的属性拷贝 ,可以通过ExcelReader中添加的数据集直接获取
     */
    public static <T> List<T> readFirstSheetExcel(MultipartFile excel, Class<T> rowType) {
        ExcelReader reader = getReader(excel);
        if (reader == null) {
            return new ArrayList<>();
        }
        return readExcel(reader, rowType, 0);
    }

    /**
     * 读取某个 sheet 的 Excel
     *
     * @param excel    文件
     * @param rowModel 实体类映射
     * @param sheetNo  sheet 的序号 从1开始
     * @return Excel 数据 list
     */
    public static <T> List readExcel(MultipartFile excel, Class<T> rowModel, int sheetNo) {
        Class[] classes = {rowModel};
        return EasyExcelUtil.readExcel(excel, sheetNo, classes)[0];
    }

    /**
     * 导出 Excel :一个 sheet,带表头
     * 自定义WriterHandler 可以定制行列数据进行灵活化操作
     *
     * @param response  HttpServletResponse
     * @param list      数据 list
     * @param fileName  导出的文件名
     * @param sheetName 导入文件的 sheet 名
     */
    public static <T> void writeExcel(HttpServletResponse response, List<T> list,
                                      String fileName, String sheetName, ExcelTypeEnum excelTypeEnum) {
        if (sheetName == null || "".equals(sheetName)) {
            sheetName = "sheet1";
        }
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        EasyExcel.write(getOutputStream(fileName, response, excelTypeEnum), list.get(0).getClass()).sheet(sheetName).doWrite(list);
    }

    /**
     * 导出 Excel :一个 sheet,带表头
     * 自定义WriterHandler 可以定制行列数据进行灵活化操作
     *
     * @param response HttpServletResponse
     * @param list     数据 list
     * @param fileName 导出的文件名
     */
    public static <T> void writeExcel(HttpServletResponse response, List<T> list,
                                      String fileName, ExcelTypeEnum excelTypeEnum) {
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        String sheetName = list.get(0).getClass().getAnnotation(SheetName.class).value();
        sheetName = EasyExcelUtil.isNotBlank(sheetName) ? sheetName : "sheet1";
        EasyExcel.write(getOutputStream(fileName, response, excelTypeEnum), list.get(0).getClass()).sheet(sheetName).doWrite(list);
    }

    /**
     * 导出 Excel :一个 sheet,带表头
     * 自定义WriterHandler 可以定制行列数据进行灵活化操作
     *
     * @param response HttpServletResponse
     * @param list     数据 list
     * @param fileName 导出的文件名
     */
    public static <T> void writeExcelPrint(HttpServletResponse response, List<T> list,
                                      String fileName, ExcelTypeEnum excelTypeEnum) {
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        String sheetName = list.get(0).getClass().getAnnotation(SheetName.class).value();
        sheetName = EasyExcelUtil.isNotBlank(sheetName) ? sheetName : "sheet1";
        EasyExcel.write(getOutputStreamPrint(fileName, response, excelTypeEnum), list.get(0).getClass()).sheet(sheetName).doWrite(list);
    }

    /**
     * 导出 Excel :一个 sheet,带表头
     * 自定义WriterHandler 可以定制行列数据进行灵活化操作
     *
     * @param response HttpServletResponse
     * @param fileName 导出的文件名
     */
    public static void writeExcel(HttpServletResponse response, String fileName,
                                  ExcelTypeEnum excelTypeEnum, List... lists) {
        ExcelWriter excelWriter = null;
        try {
            excelWriter = EasyExcel.write(getOutputStream(fileName, response, excelTypeEnum)).build();
            for (int count = 0; count < lists.length; count++) {
                if (CollectionUtils.isEmpty(lists[count])) {
                    continue;
                }
                String sheetName = lists[count].get(0).getClass().getAnnotation(SheetName.class).value();
                sheetName = EasyExcelUtil.isNotBlank(sheetName) ? sheetName : "sheet" + (count + 1);
                WriteSheet writeSheet = EasyExcel.writerSheet(count, sheetName)
                        .head(lists[count].get(0).getClass())
                        .build();
                excelWriter.write(lists[count], writeSheet);
            }
        } finally {
            if (excelWriter != null) {
                excelWriter.finish();
            }
        }

    }


    /**
     * 导出文件时为Writer生成OutputStream
     */
    private static OutputStream getOutputStream(String fileName, HttpServletResponse response, ExcelTypeEnum excelTypeEnum) {
        //创建本地文件
        String filePath = fileName + excelTypeEnum.getValue();
        try {
//            response.setContentType("application/vnd.ms-excel");
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filePath, "UTF-8"));
            return response.getOutputStream();
        } catch (IOException e) {
            throw new AppException("创建文件失败!");
        }
    }

    /**
     * 导出文件时为Writer生成OutputStream
     */
    private static OutputStream getOutputStreamPrint(String fileName, HttpServletResponse response, ExcelTypeEnum excelTypeEnum) {
        //创建本地文件
        String filePath = fileName + excelTypeEnum.getValue();
        try {
            response.setContentType("application/vnd.ms-excel");
            //response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filePath, "UTF-8"));
            return response.getOutputStream();
        } catch (IOException e) {
            throw new AppException("创建文件失败!");
        }
    }

    /**
     * 返回 ExcelReader
     *
     * @param excel 需要解析的 Excel 文件
     */
    public static ExcelReader getReader(MultipartFile excel) {
        String fileName = excel.getOriginalFilename();
        if (fileName == null) {
            throw new AppException("文件格式错误!");
        }
        if (!fileName.toLowerCase().endsWith(ExcelTypeEnum.XLS.getValue()) && !fileName.toLowerCase().endsWith(ExcelTypeEnum.XLSX.getValue())) {
            throw new AppException("文件格式错误!");
        }
        InputStream inputStream;
        try {
            inputStream = excel.getInputStream();
            return EasyExcel.read(inputStream).build();
        } catch (IOException e) {
            //do something
        }
        return null;
    }

    /**
     * 利用BeanCopy转换list
     */
    public static <T> List<T> getExtendsBeanList(List<?> list, Class<T> typeClazz) {
        return MyBeanCopy.convert(list, typeClazz);
    }

    private static boolean isBlank(final CharSequence cs) {
        int strLen;
        if (cs == null || (strLen = cs.length()) == 0) {
            return true;
        }
        for (int i = 0; i < strLen; i++) {
            if (!Character.isWhitespace(cs.charAt(i))) {
                return false;
            }
        }
        return true;
    }

    public static boolean isNotBlank(final CharSequence cs) {
        return !isBlank(cs);
    }



	/**
	 * 导出 Excel :一个 sheet,带表头
	 *
	 *
	 * @param response  HttpServletResponse
	 * @param list      数据 list
	 * @param fileName  导出的文件名
	 * @param writeHandler  自定义WriterHandler 可以定制行列数据进行灵活化操作
	 */
	public static <T> void writeExcelWithWriteHandler(HttpServletResponse response, List<T> list,
											  String fileName,ExcelTypeEnum excelTypeEnum, WriteHandler writeHandler) {
		if (CollectionUtils.isEmpty(list)) {
			return;
		}
		String sheetName = list.get(0).getClass().getAnnotation(SheetName.class).value();
		if (sheetName == null || "".equals(sheetName)) {
			sheetName = "sheet1";
		}
		EasyExcel.write(getOutputStream(fileName, response, excelTypeEnum), list.get(0).getClass()).sheet(sheetName).registerWriteHandler(writeHandler).doWrite(list);
	}
}

2.5LeftAlignCellWriteHandler(自定义样式)

package com.dahuatech.dms.utils;

import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.Workbook;

import java.util.List;

public class LeftAlignCellWriteHandler implements CellWriteHandler {

    @Override
    public int order() {
        return 50001;
    }

    @Override
    public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
            // 获取Workbook对象
            WriteWorkbookHolder writeWorkbookHolder = writeSheetHolder.getParentWriteWorkbookHolder();
            Workbook workbook = writeWorkbookHolder.getWorkbook();
            // 创建一个新的CellStyle
            CellStyle cellStyle = workbook.createCellStyle();
            // 设置水平对齐为左对齐
            cellStyle.setAlignment(HorizontalAlignment.LEFT);
            // 设置自动换行
            cellStyle.setWrapText(true);
            // 应用CellStyle到当前单元格
            cell.setCellStyle(cellStyle);
    }

}

3.使用

3.1下载导入模板

/**
 * 下载导入模板
 *
 * @param
 * @return
 */
@ApiResponse(description = "下载导入模板", content = @Content(mediaType = "application/json"))
@GetMapping("/importTemplate")
public void importTemplate(HttpServletResponse response){
    String fileName = "货主导入模板";
    List<SysCargoOwnerTemplate> sysCargoOwnerTemplates = new ArrayList<>();
    sysCargoOwnerTemplates.add(new SysCargoOwnerTemplate());
    EasyExcelUtil.writeExcel(response, sysCargoOwnerTemplates, fileName, ExcelTypeEnum.XLSX);
}

3.2导出

/**
 * 导出
 */
@ApiResponse(description = "导出", content = @Content(mediaType = "application/json"))
@GetMapping("/export")
public void export(HttpServletResponse response,SysCargoOwnerDTO sysCargoOwnerDTO)  {
    String param = JSON.toJSONString(sysCargoOwnerDTO);
    List<SysCargoOwner> export = sysCargoOwnerService.export(sysCargoOwnerDTO);
    List<SysCargoOwnerExcel> excels = DozerUtil.convertList(export, SysCargoOwnerExcel.class);
    EasyExcelUtil.writeExcel(response, excels, "货主导出", ExcelTypeEnum.XLSX);
}

3.3导入

/**
 * 导入
 *
 * @param
 * @return
 */
@ApiResponse(description = "导入", content = @Content(mediaType = "application/json"))
@PostMapping("/imports")
@ResponseBody
public Result<Boolean> imports(MultipartHttpServletRequest request) {
    Iterator<String> itr = request.getFileNames();
    String uploadedFile = itr.next();
    List<MultipartFile> files = request.getFiles(uploadedFile);
    if (CollectionUtils.isEmpty(files)) {
        return Result.error("文件为空");
    }
    // 1.读取第一个sheet文件
    List<SysCargoOwnerTemplate> list = EasyExcelUtil.readFirstSheetExcel(files.get(0), SysCargoOwnerTemplate.class);
    if (list.size() == 0) {
        return Result.error( "读取数据为空!");
    }
    return Result.success(sysCargoOwnerService.imports(list));
}

3.4导入特殊需求

3.4.1需求:excel的第一行是文字备注,第二行是标题,要求删除或不删除备注都可以导入成功。
3.4.2实现Controller层
/**
     * 导入
     *
     * @param
     * @return
     */
    @PostMapping("/imports")
    @ResponseBody
    public Boolean imports(MultipartHttpServletRequest request) throws IOException {
        Iterator<String> itr = request.getFileNames();
        String uploadedFile = itr.next();
        List<MultipartFile> files = request.getFiles(uploadedFile);
        if (CollectionUtils.isEmpty(files)) {
            throw new RuntimeException("aa");
        }
        MultipartFile file = files.get(0);
        int startRow = checkTitleRow(file); // 检查哪一行包含 "id"
        // 1.读取第一个sheet文件
        List<entiten2> list = EasyExcelUtil.readFirstSheetExcel(files.get(0), entiten2.class,startRow ,startRow);
        if (list.size() == 0) {
            throw new RuntimeException("aa");
        }
        System.out.println(list);
        return true;
    }

    /**
     * 通过监听器判断标题行是第一行还是第二行
     * @param excel
     * @return
     * @throws IOException
     */
    public static int checkTitleRow(MultipartFile excel) throws IOException {
        ExcelHeadListener listener = new ExcelHeadListener();
        EasyExcel.read(excel.getInputStream(), entiten2.class, listener)
                .headRowNumber(1) // 检查前两行
                .sheet().doRead();
        return listener.isIdFound() ? 1 : 2; // 如果第一行找到id,返回1;否则返回2
    }
3.4.3ExcelHeadListener 监听标题在第几行
package com.example.demo.utils;

import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.example.demo.entiten2;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author
 */
public class ExcelHeadListener<T> extends AnalysisEventListener<entiten2> {

    private boolean idFound = false;

    @Override
    public void invoke(entiten2 data, AnalysisContext context) {
        // 这个方法在这里不会被调用,因为我们只关心标题行
    }

    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        if (!idFound) {
            //获取excel第一行数据判断第一行是否是表头
            for (String head : headMap.values()) {
                if ("id".equals(head)) {      // 通过标题名判断,是否是标题行
                    idFound = true;
                    break;
                }
            }
        }
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // Do nothing
    }

    public boolean isIdFound() {
        return idFound;
    }
}
3.4.4在工具类EasyExcelUtil中添加新的实现readFirstSheetExcel方法
    public static <T> List<T> readFirstSheetExcel(MultipartFile excel, Class<T> rowType,int headRowNumber,int startRow) {
        ExcelReader reader = getReader(excel);
        if (reader == null) {
            return new ArrayList<>();
        }
        return readExcel(reader, rowType, 0,headRowNumber,startRow);//headRowNumber是从excel第几行开始读标题,startRow是监听校验标题字段从第行开始
    }
3.4.5在工具类EasyExcelUtil中添加新的实现readExcel方法
    public static <T> List<T> readExcel(ExcelReader reader, Class<T> rowModel, int sheetNo, int headRowNumber,int startRow) {
        if (reader == null) {
            return new ArrayList<>();
        }
        ExcelListener<T> excelListener = new ExcelListener<>(rowModel,startRow);
        reader.read(new ArrayList<ReadSheet>() {{
            add(EasyExcel.readSheet(sheetNo)
                    .headRowNumber(headRowNumber) // 从第几行开始读数据
                    .head(rowModel)
                    .registerReadListener(excelListener)
                    .build());
        }});
        return getExtendsBeanList(excelListener.getDataList(), rowModel);
    }
3.4.6在ExcelListener监听中添加判断标题的属性,和构造方法,获取标题行
package com.example.demo.utils;

import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author
 */
public class ExcelListener<T> extends AnalysisEventListener<T> {

    private final List<Object> dataList = new ArrayList<>();

    private static final Integer maxSize = 10000;

    private Class<T> rowModel;

    private int startRow = 1; //改动位置

    public ExcelListener(Class<T> rowModel) {
        this.rowModel = rowModel;
    }

    /**
     * 改动位置,用来获取导入的标题行
     * @param rowModel
     * @param startRow
     */
    public ExcelListener(Class<T> rowModel,int startRow) {
        this.rowModel = rowModel;
        this.startRow = startRow;
    }

    /**
     * 通过 AnalysisContext 对象还可以获取当前 sheet,当前行等数据
     */
    @Override
    public void invoke(T object, AnalysisContext context) {


        if(!checkObjAllFieldsIsNull(object)) {
            dataList.add(object);
        }
        if (dataList.size() > maxSize) {
            throw new RuntimeException("最大只能导入10000行数据");
        }
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        //do something
    }

    private static final String SERIAL_VERSION_UID = "serialVersionUID";

    /**
     * 判断对象中属性值是否全为空
     */
    private static boolean checkObjAllFieldsIsNull(Object object) {
        if (null == object) {
            return true;
        }
        try {
            for (Field f : object.getClass().getDeclaredFields()) {
                f.setAccessible(true);
                //只校验带ExcelProperty注解的属性
                ExcelProperty property = f.getAnnotation(ExcelProperty.class);
                if(property == null || SERIAL_VERSION_UID.equals(f.getName())){
                    continue;
                }
                if (f.get(object) != null && EasyExcelUtil.isNotBlank(f.get(object).toString())) {
                    return false;
                }
            }
        } catch (Exception e) {
            //do something
        }
        return true;
    }

    public List<?> getDataList() {
        return dataList;
    }

    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        int rowIndex = context.readRowHolder().getRowIndex();  //改动位置
        if (rowIndex == startRow - 1) {                         //改动位置
            Field[] fields = rowModel.getDeclaredFields();
            int length = fields.length;
            List<String> headNameList = new ArrayList<>();
            for (int i = 0; i < length; i++) {
                ExcelProperty annotation = fields[i].getAnnotation(ExcelProperty.class);
                if (annotation != null) {
                    headNameList.add(annotation.value()[0]);
                }
            }
            int size = headNameList.size();
            if (headMap.size() != size) {
                throw new RuntimeException("导入模板错误!请下载正确的导入模板");
            }
            for (int i = 0; i < size; i++) {
                if (!headNameList.get(i).equals(headMap.get(i))) {
                    throw new RuntimeException("导入模板错误!请下载正确的导入模板");
                }
            }
        }   //改动位置
    }
}
3.4.7(注意:需要创新新的对应标题行的实体类,下面是例子)
package com.example.demo;

import com.alibaba.excel.annotation.ExcelProperty;
import com.example.demo.utils.SheetName;
import lombok.Data;

@Data
@SheetName("moban")
public class entiten2 {
    private static final String aaa = "biatossa";

//    @ExcelProperty({aaa,"id"})
    @ExcelProperty("id")
    private String id;
//    @ExcelProperty({aaa,"name"})
    @ExcelProperty("name")
    private String name;
//    @ExcelProperty({aaa,"age"})
    @ExcelProperty("age")
    private String age;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值