easyexcel导出导入下载

序言

本文介绍 导入、导出 主要功能操作 ,包括了以压缩包形式导出、模板导出、以及写导出、多个sheet页导出、复杂树形,层级导出、自定义合并单元格,动态填充头等操作示例

1.介绍

Java解析、生成Excel比较有名的框架有Apache
poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。
easyexcel重写了poi对07版Excel的解析,一个3M的excel用POI
sax解析依然需要100M左右内存,改用easyexcel可以降低到几M,并且再大的excel也不会出现内存溢出;03版依赖POI的sax模式,在上层做了模型转换的封装,让使用者更加简单方便

1.1网址

官方网站:https://easyexcel.opensource.alibaba.com/
github地址:https://github.com/alibaba/easyexcel
gitee地址:https://gitee.com/easyexcel/easyexcel

1.普通导出

1.1引入jar包

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

1.2处理导出列

@ContentRowHeight(20)
@HeadRowHeight(30)
@ColumnWidth(25)
@Data
public class ModelExtractionExcel {
    @ExcelProperty(
            value = {"字段名称"},
            index = 0
    )
    private String key;

    @ExcelProperty(
            value = {"字段值"},
            index = 1
    )
    private String value;
}

1.3 具体导出逻辑

	public void downloadModelExtraction(HttpServletResponse response,Long id,String name) {
        ExcelWriter excelWriter = null;
        try {
            //查询提取信息
            List<ModelOperationExtractionDtl> attachmentExtractionList = modelOperationExtractionDtlMapperExt.getAttachmentExtraction(id,null);
            List<ModelExtractionExcel> modelExtractionExcels = BeanUtil.copyToList(attachmentExtractionList, ModelExtractionExcel.class);
            //构造模板导入信息
            response.setContentType(Constants.CONTENT_TYPE_EXCEL);
            response.setCharacterEncoding(Charsets.UTF_8.name());
            // 防止中文乱码
            String encodeFileName = URLEncoder.encode(name, Charsets.UTF_8);
            response.setHeader(Constants.FIELD_CONTENT_DISPOSITION, "attachment;filename=" + encodeFileName + Constants.FIELD_SUFFIX_XLSX);
            excelWriter = EasyExcel.write(response.getOutputStream()).build();
            WriteSheet importSheet = EasyExcel.writerSheet(0, "提取数据").head(ModelExtractionExcel.class).build();
            //write
            excelWriter.write(modelExtractionExcels, importSheet);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (excelWriter != null) {
                excelWriter.finish();
            }
        }

    }

2.普通导入

   @PostMapping("/import")
    @Operation(summary = "导入")
    public Result imports(@RequestParam("file") MultipartFile file) throws IOException {

        InputStream inputStream = file.getInputStream();
        List<CoachRecordImport> reqCustomerImports = EasyExcel.read(inputStream)
                .head(CoachRecordImport.class)
                // 注册监听器,可以在这里校验字段
                .registerReadListener(new `CustomerCoachImportListener`(coachService))
                // 设置sheet,默认读取第一个
                .sheet(Constant.SHEET_NUM)
                // 设置标题所在行数
                .headRowNumber(Constant.HEAD_ROW_NUMBER)
                .doReadSync();

        return coachService.importAddCoach(reqCustomerImports);

    }
@Data
public class CoachRecordImport implements Serializable {

    @Serial
    private static final long serialVersionUID = -3119830383757608829L;
    @ExcelProperty(index = 0, value = "姓名")
    private String name;
    @ExcelProperty(index = 1, value = "性别", converter = SexConverter.class)
    private String gender;
    @ExcelProperty(index = 2, value = "身份证号")
    private String idCard;
    @ExcelProperty(index = 3, value = "年龄")
    private Integer age;
    @ExcelProperty(index = 4, value = "手机号")
    private String phoneNumber;
    @ExcelProperty(index = 5, value = "服务课程")
    private String serviceCourseIds;

}```

```java
package com.insoundai.childrenplatform.operation.service.listener;

import cn.hutool.core.util.IdcardUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.insoundai.childrenplatform.operation.common.exception.ServiceException;
import com.insoundai.childrenplatform.operation.pojo.impt.CoachRecordImport;
import com.insoundai.childrenplatform.operation.service.CoachService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Field;
import java.util.Map;
import java.util.Objects;

/**
 * @Author: GZ
 * @CreateTime: 2022-09-19  14:52
 * @Description: 自定义导入监听类 对模板与字段进行校验
 * @Version: 1.0
 */
@Slf4j
public class CustomerCoachImportListener extends AnalysisEventListener<CoachRecordImport> {
    public CoachService coachService;

    public CustomerCoachImportListener(CoachService coachService) {
        this.coachService = coachService;
    }

    /**
     * 在这里进行模板的判断
     * @param headMap 存放着导入表格的表头,键是索引,值是名称
     * @param context
     */
    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        int count = 0;
        // 获取数据实体的字段列表
        Field[] fields = CoachRecordImport.class.getDeclaredFields();
        // 遍历字段进行判断
        for (Field field : fields) {
            // 获取当前字段上的ExcelProperty注解信息
            ExcelProperty fieldAnnotation = field.getAnnotation(ExcelProperty.class);
            // 判断当前字段上是否存在ExcelProperty注解
            if (fieldAnnotation != null) {
                ++count;
                // 存在ExcelProperty注解则根据注解的index索引到表头中获取对应的表头名
                String headName = headMap.get(fieldAnnotation.index());
                // 判断表头是否为空或是否和当前字段设置的表头名不相同
                if (StringUtils.isEmpty(headName) || !headName.equals(fieldAnnotation.value()[0])) {
                    // 如果为空或不相同,则抛出异常不再往下执行
                    throw new RuntimeException("模板错误,请检查导入模板");
                }
            }
        }
        // 判断用户导入表格的标题头是否完全符合模板
        if (count != headMap.size()) {
            throw new RuntimeException("模板错误,请检查导入模板");
        }
    }


    @Override
    public void invoke(CoachRecordImport coachRecordImport, AnalysisContext analysisContext) {
        System.out.println("CustomerDailyImportListener------->");
        log.info("----------CustomerImportListener is start------------");

        //校验身份号是否合法
        if (Objects.nonNull(coachRecordImport)) {
            if (!Objects.isNull(coachRecordImport.getIdCard())) {
                boolean validCard = IdcardUtil.isValidCard(coachRecordImport.getIdCard());
                if (!validCard) {
                    throw new ServiceException("导入失败,该身份证:" + coachRecordImport.getIdCard() + "不合法");
                }
            }
        }

    }

    @Override
    public void onException(Exception exception, AnalysisContext context) throws Exception {
        // ExcelDataConvertException:当数据转换异常的时候,会抛出该异常,此处可以得知第几行,第几列的数据
        if (exception instanceof ExcelDataConvertException) {
            Integer columnIndex = ((ExcelDataConvertException) exception).getColumnIndex() + 1;
            Integer rowIndex = ((ExcelDataConvertException) exception).getRowIndex() + 1;
            String message = "第" + rowIndex + "行,第" + columnIndex + "列" + "数据格式有误,请核实";
            throw new RuntimeException(message);
        } else if (exception instanceof RuntimeException) {
            throw exception;
        } else {
            super.onException(exception, context);
        }
    }


    /**
     * @param analysisContext
     * @description: 解析完成全步回调
     * @author: GZ
     * @date: 2022/09/19 14:56
     * @return: void
     **/

    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {

    }


}

字段转换
public class SexConverter implements Converter<String> {

    @Override
    public Class supportJavaTypeKey() {
        return Converter.super.supportJavaTypeKey();
    }

    @Override
    public CellDataTypeEnum supportExcelTypeKey() {
        return CellDataTypeEnum.STRING;
    }

    @Override
    public String convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty,
                                    GlobalConfiguration globalConfiguration) throws Exception {
        return "男".equals(cellData.getStringValue()) ? "1" : "0";
    }


}

3.导出全部(以压缩包形式导出)

 public  void  downloadAllModelExtraction(HttpServletResponse response, Long modelId) {
        List<ExcelUtils.ExcelData> excelDataList = Lists.newArrayList();

        //查询模型提取数据
        List<ModelOperationExtractionDtl> attachmentExtractionList = modelOperationExtractionDtlMapperExt.getAttachmentExtraction(null,modelId);
        Assert.isTrue(!CollectionUtils.isEmpty(attachmentExtractionList),"提取数据为空,还末提取完成");
        //按照附近id进行分组
        Map<Long, List<ModelOperationExtractionDtl>> attachmentMap= attachmentExtractionList.stream()
                .collect(Collectors.groupingBy(ModelOperationExtractionDtl::getOperationId));
        attachmentMap.forEach((key,value)->{
            ExcelUtils.ExcelData excel = new ExcelUtils.ExcelData();
            List<ModelExtractionExcel> modelExtractionList = BeanUtil.copyToList(value, ModelExtractionExcel.class);
            //设置文件名
            excel.setFilename(reFileName(value,attachmentExtractionList,key));
            //不设置下载模板
            excel.setTemplateFilename(null);
            //设置sheet数据
            excel.addShellData(new ExcelUtils.ExcelShellData<>(modelExtractionList, "提取数据", ModelExtractionExcel.class));
            excelDataList.add(excel);
        });
        //通过模型id查询模型名称
        String name= modelService.getModelName(modelId);
        String zipName=name+ DateUtil.now();
        ExcelUtils.exportZip(zipName, excelDataList, response);

    }
package com.insound.license.recognition.backend.util;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.google.common.base.Charsets;
import com.insound.license.recognition.backend.pojo.constant.Constants;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;

import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @Author: GZ
 * @CreateTime: 2022-12-29  14:44
 * @Description: excel工具类
 * @Version: 1.0
 */
public class ExcelUtils {
    /**
     * 导出多个sheet到多个excel文件,并压缩到一个zip文件 支持模板和直接创建
     */
    public static void exportZip(String zipFilename, List<ExcelData> excelDataList, HttpServletResponse response) {
        if (zipFilename == null || zipFilename.isEmpty()) {
            zipFilename = "export";
        } else if (zipFilename.toLowerCase(Locale.ROOT).endsWith(".zip")) {
            zipFilename = zipFilename.substring(0, zipFilename.length() - 4);
        }
        if (excelDataList == null || excelDataList.isEmpty()) {
            throw new RuntimeException("导出数据为空!");
        }
        try {
            zipFilename = URLEncoder.encode(zipFilename, Charsets.UTF_8);
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + zipFilename + ".zip");
            response.setContentType("application/x-msdownload");
            response.setCharacterEncoding("utf-8");
            //开始存入
            try (ZipOutputStream zipOut = new ZipOutputStream(response.getOutputStream())) {
                try {
                    for (ExcelData excelData : excelDataList) {
                        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                        ExcelWriter excelWriter = null;
                        try {
                            ExcelWriterBuilder builder = EasyExcel.write(outputStream).autoCloseStream(false)
                                    // 自动适配
                                    .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                                    // 大数值自动转换 防止失真
                                    .registerConverter(new ExcelBigNumberConvert());
                            if (excelData.getTemplateFile() != null) {
                                builder.withTemplate(excelData.getTemplateFile());
                            }
                            excelWriter = builder.build();
                            zipOut.putNextEntry(new ZipEntry(excelData.getFilename()));
                            if (excelData.getTemplateFile() != null) {
                                for (ExcelShellData<?> shellData : excelData.getShellDataList()) {
                                    WriteSheet writeSheet = EasyExcel.writerSheet(shellData.getSheetName()).build();
                                    excelWriter.fill(shellData.getList(), writeSheet);
                                }
                            } else {
                                //开始写入excel
                                for (ExcelShellData<?> shellData : excelData.getShellDataList()) {
                                    WriteSheet writeSheet = EasyExcel.writerSheet(shellData.getSheetName()).head(shellData.getClazz()).build();
                                    excelWriter.write(shellData.getList(), writeSheet);
                                }
                            }
                        } catch (Exception e) {
                            throw new RuntimeException("导出Excel异常", e);
                        } finally {
                            if (excelWriter != null) {
                                excelWriter.finish();
                            }
                        }
                        outputStream.writeTo(zipOut);
                        zipOut.closeEntry();
                    }

                } catch (Exception e) {
                    throw new RuntimeException("导出Excel异常", e);
                }
            }
        } catch (IOException e) {
            throw new RuntimeException("导出Excel异常", e);
        }
    }
    /**
     * excel数据
     */
    public static class ExcelData {
        private List<ExcelShellData<?>> shellDataList = new ArrayList<>();
        private String filename = "export.xlsx";
        private String templateFilename;

        public List<ExcelShellData<?>> getShellDataList() {
            return shellDataList;
        }

        public void setFilename(String filename) {
            this.filename = filename;
        }

        public String getTemplateFilename() {
            return templateFilename;
        }

        public void setTemplateFilename(String templateFilename) {
            this.templateFilename = templateFilename;
        }

        public File getTemplateFile() {
            if (templateFilename == null) {
                return null;
            }
            File templateFile = new File(templateFilename);
            if (templateFile.exists() && templateFile.isFile()) {
                return templateFile;
            }
            Resource resource = new ClassPathResource(templateFilename);
            if (resource.exists()) {
                File file = null;
                try {
                    file = resource.getFile();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                if (file != null) {
                    if (file.exists() && file.isFile()) {
                        return file;
                    }
                    return null;
                }
            }
            return null;
        }

        public void setShellDataList(List<ExcelShellData<?>> shellDataList) {
            if (shellDataList != null) {
                this.shellDataList = shellDataList;
            }
        }

        public String getFilename() {
            if (filename == null || filename.isEmpty()) {
                filename = "export.xlsx";
            } else {
                String fn = filename.toLowerCase(Locale.ROOT);
                if (!(fn.endsWith(".xlsx") || fn.endsWith(".xls"))) {
                    filename = filename + ".xlsx";
                }
            }
            return filename;
        }

        public void addShellData(ExcelShellData<?> excelShellData) {
            this.shellDataList.add(excelShellData);
        }
    }
    /**
     * sheet数据
     */
    public static class ExcelShellData<T> {
        private List<T> list;
        private String sheetName;
        private Class<T> clazz;

        public ExcelShellData(List<T> list, String sheetName, Class<T> clazz) {
            this.list = list;
            this.sheetName = sheetName;
            this.clazz = clazz;
        }

        public List<T> getList() {
            return list;
        }

        public void setList(List<T> list) {
            this.list = list;
        }

        public String getSheetName() {
            return sheetName;
        }

        public void setSheetName(String sheetName) {
            this.sheetName = sheetName;
        }

        public Class<T> getClazz() {
            return clazz;
        }

        public void setClazz(Class<T> clazz) {
            this.clazz = clazz;
        }
    }
    /**
     * Excel 数值长度位15位 大于15位的数值转换位字符串
     */
    public static class ExcelBigNumberConvert implements Converter<Long> {

        @Override
        public Class<Long> supportJavaTypeKey() {
            return Long.class;
        }

        @Override
        public CellDataTypeEnum supportExcelTypeKey() {
            return CellDataTypeEnum.STRING;
        }

        @Override
        public Long convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
            Object data = cellData.getData();
            if (data == null) {
                return null;
            }
            String s = String.valueOf(data);
            if (s.matches("^\\d+$")) {
                return Long.parseLong(s);
            }
            return null;
        }

        @Override
        public WriteCellData<Object> convertToExcelData(Long object, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
            if (object != null) {
                String str = object.toString();
                if (str.length() > Constants.EXCEL_VALUE_LENGTH) {
                    return new WriteCellData<>(str);
                }
            }
            WriteCellData<Object> cellData = new WriteCellData<>(new BigDecimal(object));
            cellData.setType(CellDataTypeEnum.NUMBER);
            return cellData;
        }

    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我叫果冻

你的鼓励将是我创作的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值