EasyExcel学习总结

EasyExcel详解(结合官方文档) EasyExcel的学习分享 阅读详情

1、什么事EasyExcel?

	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模式,在上层做了模型转换的封装,让使用者更加简单方便
	

2、EasyExcel特点

➢ 使用简单
➢ 高性能
➢ 低消耗

3、EasyExcel读数据

3.1、少量数据读取

➢ 读操作

    /**
     * 读取指定路径文件
     *
     * @param filePath 文件路径
     * @param clazz 映射实体类型
     * @param <T> 实体
     * @return
     */
    public static <T> List<T> read(String filePath, final Class<T> clazz) {

        if (ObjectUtils.isNotEmpty(filePath)) {
            throw new ServiceException("路径文件出错了,为空");
        }

        // DataListAnalysis 不能被spring管理
        DataListAnalysis<T> listener = new DataListAnalysis<>();

        // 默认第一个sheet 文件流会自动关闭
        EasyExcel.read(filePath, clazz, listener).sheet().doRead();

        return listener.getData();
    }
    /**
     * 读取excel流,数据量不是很大,可一次全量读取
     *
     * @param inputStream 文件流
     * @param clazz 映射实体类型
     * @param <T> 实体
     * @return
     */
    public static <T> List<T> read(InputStream inputStream, final Class<T> clazz) {
        if (inputStream == null) {
            throw new ServiceException("解析出错了,文件流是null");
        }

        // DataListAnalysis 不能被spring管理
        DataListAnalysis<T> listener = new DataListAnalysis<>();

        // 默认第一个sheet 文件流会自动关闭
        EasyExcel.read(inputStream, clazz, listener).sheet().doRead();
        return listener.getData();
    }

➢ 读取监听器

	/**
     * 读取文件的解析器
     *
     * @param <T>
     */
    public class DataListAnalysis<T> extends AnalysisEventListener<T> {

        private List<T> data = new ArrayList<>();

        /**
         * 每解析一行数据事件调度中心都会通知到这个方法, 订阅者1
         *
         * @param t               解析的每行数据
         * @param analysisContext
         */
        @Override
        public void invoke(T t, AnalysisContext analysisContext) {
            data.add(t);
        }

        /**
         * excel文件解析完成后,事件调度中心会通知到该方法, 订阅者2
         *
         * @param analysisContext
         */
        @Override
        public void doAfterAllAnalysed(AnalysisContext analysisContext) {
            log.info("文件读取完成");
        }

        public List<T> getData() {
            return data;
        }
    }

3.2、大流数据读取

大量数据读取时,为防止OOM,需要分批将读取的数据先处理,一般进行临时性持久化操作


	/**
     * 读取大数据文件的解析器
     *
     * @param <T>
     */
    public class BigDataListAnalysis<T> extends AnalysisEventListener<T> {

        private List<T> data = new ArrayList<>();

        /**
         * 默认每10000条数据处理一次
         */
        private int batchCount = 10000;

        /**
         * 数据处理器,一般进行临时性持久化操作
         */
        private BigDataListHandle handle;

        /**
         * 构造器1
         */
        public BigDataListAnalysis(BigDataListHandle handle) {
            this.handle = handle;
        }

        /**
         * 构造器2
         */
        public BigDataListAnalysis(BigDataListHandle handle, Integer batchCount) {
            this.handle = handle;

            if (ObjectUtils.isNotEmpty(batchCount) && batchCount > 0) {
                this.batchCount = batchCount;
            }
        }

        /**
         * 每解析一行数据事件调度中心都会通知到这个方法, 订阅者1
         *
         * @param t               解析的每行数据
         * @param analysisContext
         */
        @Override
        public void invoke(T t, AnalysisContext analysisContext) {

            data.add(t);

            // 达到batchCount了,需要处理
            // 注意,如果最后一次未达到batchCount需到doAfterAllAnalysed方法中进行处理
            if (data.size() >= batchCount) {

                // 处理数据
                handle.handle(data);

                // 处理完成清空 list
                data.clear();
            }
        }

        /**
         * excel文件解析完成后,事件调度中心会通知到该方法, 订阅者2
         *
         * @param analysisContext
         */
        @Override
        public void doAfterAllAnalysed(AnalysisContext analysisContext) {

            // 处理最后一批数据
            if (ObjectUtils.isNotEmpty(data)) {
                handle.handle(data);
            }

            log.info("文件读取完成");
        }

        public List<T> getData() {
            return data;
        }

    }

监听器可以进一步优化,更方便使用

	/**
     * 获取读取Excel的监听器对象
     * 为了解耦及减少每个数据模型bean都要创建一个监听器的臃肿, 使用泛型指定数据模型类型
     * 使用jdk8新特性中的函数式接口 Consumer
     * 可以实现任何数据模型bean的数据解析, 不用重复定义监听器
     *
     * @param consumer  处理解析数据的函数, 一般可以是数据入库逻辑的函数
     * @param threshold 阈值,达到阈值就处理一次存储的数据
     * @param <T>       数据模型泛型
     * @return 返回监听器
     */
    public static <T> AnalysisEventListener<T> getReadListener(Consumer<List<T>> consumer, int threshold) {

        return new AnalysisEventListener<T>() {

            /**
             * 存储解析的数据 T t
             * LinkedList基于双向链表实现, 插入和删除更快
             */
            List<T> dataList = new LinkedList<>();

            /**
             * 每解析一行数据事件调度中心都会通知到这个方法, 订阅者1
             * @param data 解析的每行数据
             * @param context
             */
            @Override
            public void invoke(T data, AnalysisContext context) {
                dataList.add(data);
                // 达到阈值就处理一次存储的数据
                if (dataList.size() >= threshold) {
                    consumer.accept(dataList);
                    dataList.clear();
                }
            }

            /**
             * excel文件解析完成后,事件调度中心会通知到该方法, 订阅者2
             * @param context
             */
            @Override
            public void doAfterAllAnalysed(AnalysisContext context) {
                // 最后阈值外的数据做处理
                if (dataList.size() > 0) {
                    consumer.accept(dataList);
                }
            }
        };
    }

4、EasyExcel写数据

➢ 向一个sheet中写入

    /**
     * 写出list到指定文件文,sheet默认为0
     *
     * @param outFile
     * @param list
     */
    public static<T> void write(String outFile, List<T> list) {
        Class clazz = list.get(0).getClass();

        // 新版本会自动关闭流,不需要自己操作
        EasyExcel.write(outFile, clazz).sheet().doWrite(list);
    }

    /**
     * 写出list到流中,并指定sheet名
     *
     * @param outputStream
     * @param list
     * @param sheetName
     */
    public static void write(OutputStream outputStream, List<?> list, String sheetName) {
        Class<?> clazz = list.get(0).getClass();

        // sheetName为sheet的名字,默认写第一个sheet
        EasyExcel.write(outputStream, clazz).sheet(sheetName).doWrite(list);
    }

➢ 向多个sheet中写入

    /**
     * 写出到指定文件文,并向多个sheet写入
     *
     * @param outFile
     * @param map     key 为sheet名,值为数据
     */
    public static void writerSheets(String outFile, HashMap<Class, List> map) {

        // 关闭自动关闭流
        ExcelWriter excelWriter = EasyExcel.write(outFile).autoCloseStream(false).build();

        try {
            int sheetCount = 0;
            for (Class clazz : map.keySet()) {
                WriteSheet sheet = EasyExcel.writerSheet(sheetCount++, clazz.getName()).head(clazz).build();
                excelWriter.write(map.get(clazz), sheet);
            }
        } catch (Exception e) {
            log.error("导出异常:{}", e);
        } finally {
            if (excelWriter != null) {
                excelWriter.close();
            }
        }
    }

➢ 内容追加写入

/**
 * 写出数据到Excel,如果文件存在则追加内容
 * 
 * @param outFilePath 
 * @param clazz
 * @param list
 * @param <T>
 */
public static <T> void writeAppend(String outFilePath, Class<T> clazz, List<T> list) {
    
    File outFile = new File(outFilePath);
    File tempFile = new File(outFile.getParentFile().getAbsolutePath(), System.currentTimeMillis() + ".xlsx");

    if (outFile.exists()) {
        EasyExcel.write().needHead(false).head(clazz).withTemplate(outFile).file(tempFile).sheet().doWrite(list);
    } else {
        EasyExcel.write(outFile).head(clazz).sheet().doWrite(list);
    }

    if (tempFile.exists()) {
        outFile.delete();
        tempFile.renameTo(outFile);
    }
}

➢ 使用模板写入



➢ 写入拦截器

在导出Excel时往往还会有一些特殊的处理,比如添加水印等,这时就可以使用拦截器进行处理,
EasyExcel的拦截器有以下5个:

WriteHandler 基础拦截器,是后四个的父接口
CellWriteHandler 单元格处理拦截器
RowWriteHandler  行处理拦截器
SheetWriteHandler Sheet处理拦截器
WorkbookWriteHandler Workbook单元格处理拦截器

例如:

import cn.hutool.core.img.ImgUtil;
import com.alibaba.excel.write.handler.SheetWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
import com.chengjiang.common.core.utils.reflect.ReflectUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.openxml4j.opc.PackagePartName;
import org.apache.poi.openxml4j.opc.PackageRelationship;
import org.apache.poi.openxml4j.opc.TargetMode;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFPictureData;
import org.apache.poi.xssf.usermodel.XSSFRelation;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

/**
 * excel添加水印,
 *  easyExcel默认使用的是SXSSFWorkbook,
 *  使用时设置inMemory(true),使用 XSSFWorkbook
 *
 * @author chengjiang
 */
@Slf4j
public class CustomWaterMarkHandler implements SheetWriteHandler {

    private WaterMark watermark;

    public CustomWaterMarkHandler(WaterMark watermark) {
        this.watermark = watermark;
    }

    public CustomWaterMarkHandler(String content) {
        WaterMark waterMark = new WaterMark();
        waterMark.setContent(content);

        this.watermark = waterMark;
    }

    @Override
    public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
    }

    @Override
    public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
        try {
            BufferedImage bufferedImage = createWatermarkImage();
            Workbook workbook = writeWorkbookHolder.getWorkbook();

            if (workbook instanceof SXSSFWorkbook) {
                setWaterMarkToExcel((SXSSFWorkbook) workbook, bufferedImage);
            } else if (workbook instanceof XSSFWorkbook) {
                setWaterMarkToExcel((XSSFWorkbook) workbook, bufferedImage);
            }

        } catch (Exception e) {
            log.error("添加水印出错: {}", e);
            throw new RuntimeException("添加水印出错");
        }
    }

    private BufferedImage createWatermarkImage() {
        final Font font = watermark.getFont();
        final int width = watermark.getWidth();
        final int height = watermark.getHeight();

        String[] textArray = watermark.getContent().split(",");
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        // 背景透明 开始
        Graphics2D g = image.createGraphics();
        image = g.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);
        g.dispose();
        // 背景透明 结束
        g = image.createGraphics();
        // 设定画笔颜色
        g.setColor(new Color(Integer.parseInt(watermark.getColor().substring(1), 16)));
        // 设置画笔字体
        g.setFont(font);
        // 设定倾斜度
        g.shear(watermark.getShearX(), watermark.getShearY());

        // 设置字体平滑
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        int y = watermark.getYAxis();
        for (String s : textArray) {
            // 从画框的y轴开始画字符串.假设电脑屏幕中心为0,y轴为正数则在下方
            g.drawString(s, 0, y);
            y = y + font.getSize();
        }

        // 释放画笔
        g.dispose();
        return image;
    }

    /**
     * 将水印添加到excel
     *
     * @param workbook
     * @param bfi
     */
    private void setWaterMarkToExcel(XSSFWorkbook workbook, BufferedImage bfi) {
        //将图片添加到工作簿
        int pictureIdx = workbook.addPicture(ImgUtil.toBytes(bfi, ImgUtil.IMAGE_TYPE_PNG), Workbook.PICTURE_TYPE_PNG);

        //建立 sheet 和 图片 的关联关系
        XSSFPictureData xssfPictureData = workbook.getAllPictures().get(pictureIdx);

        for (int i = 0; i < workbook.getNumberOfSheets(); i++) {
            XSSFSheet xssfSheet = workbook.getSheetAt(i);
            PackagePartName packagePartName = xssfPictureData.getPackagePart().getPartName();
            PackageRelationship packageRelationship = xssfSheet.getPackagePart()
                    .addRelationship(packagePartName, TargetMode.INTERNAL, XSSFRelation.IMAGES.getRelation(), null);

            //添加水印到工作表
            xssfSheet.getCTWorksheet().addNewPicture().setId(packageRelationship.getId());
        }
    }

    /**
     * 给 Excel 添加水印
     *
     * @param workbook      SXSSFWorkbook
     */
    public void setWaterMarkToExcel(SXSSFWorkbook workbook, BufferedImage image) throws IOException {

        ByteArrayOutputStream imageOs = new ByteArrayOutputStream();
        ImageIO.write(image, ImgUtil.IMAGE_TYPE_PNG, imageOs);

        int pictureIdx = workbook.addPicture(imageOs.toByteArray(), XSSFWorkbook.PICTURE_TYPE_PNG);
        XSSFPictureData pictureData = (XSSFPictureData) workbook.getAllPictures().get(pictureIdx);

        //获取每个Sheet表
        for (int i = 0; i < workbook.getNumberOfSheets(); i++) {
            SXSSFSheet sheet = workbook.getSheetAt(i);

            //这里由于 SXSSFSheet 没有 getCTWorksheet() 方法,通过反射取出 _sh 属性
            XSSFSheet shReflect = (XSSFSheet) ReflectUtil.getFieldValue(sheet, "_sh");
            PackagePartName ppn = pictureData.getPackagePart().getPartName();
            String relType = XSSFRelation.IMAGES.getRelation();
            PackageRelationship pr = shReflect.getPackagePart().addRelationship(ppn, TargetMode.INTERNAL, relType, null);
            shReflect.getCTWorksheet().addNewPicture().setId(pr.getId());
        }
    }

    /**
     * 水印配置类
     */
    @Data
    public class WaterMark {

        /**
         * 水印内容
         */
        private String content = "";

        /**
         * 画笔颜色. 格式为"#RRGGBB",eg: "#C5CBCF"
         */
        private String color = "#C5CBCF";

        /**
         * 字体样式
         */
        private Font font = new Font("Microsoft YaHei", Font.BOLD, 40);

        /**
         * 水印宽度
         */
        private int width = 400;

        /**
         * 水印高度
         */
        private int height = 300;

        /**
         * 水平倾斜度
         */
        private double shearX = 0.1;

        /**
         * 垂直倾斜度
         */
        private double shearY = -0.26;

        /**
         * 字体的y轴位置
         */
        private int yAxis = 200;

    }
}

5、转换器

➢ EasyExcel自带转换器

bigdecimal
    BigDecimalBooleanConverter
    BigDecimalNumberConverter
    BigDecimalStringConverter
biginteger
    BigIntegerBooleanConverter
    BigIntegerNumberConverter
    BigIntegerStringConverter
booleanconverter
    BooleanBooleanConverter
    BooleanNumberConverter
    BooleanStringConverter
bytearray
    BoxingByteArrayImageConverter
    ByteArrayImageConverter
byteconverter
    ByteBooleanConverter
    ByteNumberConverter
    ByteStringConverter
date
    DateDateConverter
    DateNumberConverter
    DateStringConverter
doubleconverter
    DoubleBooleanConverter
    DoubleNumberConverter
    DoubleStringConverter
file
    FileImageConverter
floatconverter
    FloatBooleanConverter
    FloatNumberConverter
    FloatStringConverter
inputstream
    InputStreamImageConverter
integer
    IntegerBooleanConverter
    IntegerNumberConverter
    IntegerStringConverter
localdate
    LocalDateDateConverter
    LocalDateNumberConverter
    LocalDateStringConverter
localdatetime
    LocalDateTimeDateConverter
    LocalDateTimeNumberConverter
    LocalDateTimeStringConverter
longconverter
    LongBooleanConverter
    LongNumberConverter
    LongStringConverter
shortconverter
    ShortBooleanConverter
    ShortNumberConverter
    ShortStringConverter
string
    StringBooleanConverter
    StringErrorConverter
    StringImageConverter
    StringNumberConverter
    StringStringConverter
url
    UrlImageConverter
    

➢ 自定义转换器

自定义转换器只需实现Converter接口即可;

import cn.hutool.core.convert.Convert;
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.chengjiang.common.core.utils.excel.easyexcel.annotate.ExcelDictType;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;

/**
 * Excel 数据字典转换器
 *
 * @author chengjiang
 */
@Slf4j
public class DataDictConverter implements Converter<Object> {

    /**
     * 导入时如果字段类型匹配,执行此方法
     *
     * @return
     */
    @Override
    public Class<?> supportJavaTypeKey() {
        return Object.class;
    }

    /**
     * 导出时如果字段类型匹配,执行此方法
     *
     * @return
     */
    @Override
    public CellDataTypeEnum supportExcelTypeKey() {
        return CellDataTypeEnum.STRING;
    }

    /**
     * 导入时将Excel列值转换为java字段值
     * 
     * @param readCellData
     * @param contentProperty
     * @param globalConfiguration
     * @return
     */
    @Override
    public Object convertToJavaData(ReadCellData readCellData, ExcelContentProperty contentProperty,
                                    GlobalConfiguration globalConfiguration) {

        // 使用字典解析
        String dictType = getDictType(contentProperty);
        String dictLabel = readCellData.getStringValue();
        String dictValue = getDictValue(dictType, dictLabel);

        if (dictValue == null) {
            log.error("[转换器 DictConvert.convertToJavaData][dictType({}) 无法解析 dictLabel({})]", dictType, dictLabel);
            return null;
        }

        // 将 String 的 value 转换成对应的属性
        Class<?> fieldClazz = contentProperty.getField().getType();
        return Convert.convert(fieldClazz, dictValue);
    }

    /**
     * 导出时将java字段值转换为Excel列值
     *
     * @param object
     * @param contentProperty
     * @param globalConfiguration
     * @return
     */
    @Override
    public WriteCellData<String> convertToExcelData(Object object, ExcelContentProperty contentProperty,
                                                    GlobalConfiguration globalConfiguration) {
        // 空时,返回空
        if (object == null) {
            return new WriteCellData<>("");
        }

        // 使用字典格式化
        String dictType = getDictType(contentProperty);
        String dictValue = String.valueOf(object);

        String label = getDictLabel(dictType, dictValue);
        if (label == null) {
            log.error("[转换器 DictConvert.convertToExcelData ][dictType({}) 无法解析 dictValue({})]", dictType, dictValue);
            return new WriteCellData<>("");
        }

        return new WriteCellData<>(label);
    }

    /**
     * 获取字典类型
     *
     * @param contentProperty
     * @return
     */
    private static String getDictType(ExcelContentProperty contentProperty) {
        ExcelDictType annotation = contentProperty.getField().getAnnotation(ExcelDictType.class);
        return ObjectUtils.isNotEmpty(annotation) ? annotation.dictType() : null;
    }

    /**
     * 获取字典值
     *
     * @param dictType
     * @param dictLabel
     * @return
     */
    private static String getDictValue(String dictType, String dictLabel) {
        if (ObjectUtils.isEmpty(dictType) || ObjectUtils.isEmpty(dictLabel)) {
            return null;
        }

        // todo 执行字典匹配
        return "A";
    }

    /**
     * 获取字典label
     *
     * @param dictType
     * @param dictValue
     * @return
     */
    private static String getDictLabel(String dictType, String dictValue) {
        if (ObjectUtils.isEmpty(dictType) || ObjectUtils.isEmpty(dictValue)) {
            return null;
        }

        // todo 执行字典匹配
        return "a";
    }

}

➢ 转换器配置方式

1. 通过@ExcelProperty注解的converter属性进行配置,推荐使用

2. 在执行ExcelReaderBuilder或ExcelWriterBuilder中进行配置
EasyExcel.write(output).registerConverter();
EasyExcel.read(inputStream, clazz, listener).registerConverter()

6、实体注解

➢ 实体字段与excel列映射

@ExcelProperty

value:
用于映射Excel表头,默认值为空,必须内容全匹配才能映射
如果多个字段的value设置相同,则会进行合并
如果字段没添加@ExcelProperty注解或者value为空时,则使用字段名作为默认表头;

index:
用于映射Excel表头列序号,默认值为 -1,优先级高于value与order

order:
将字段进行排序,与excel表的列顺序映射,默认值为 Integer.MAX_VALUE

converter:
字段值转换器,默认值为自动转换

@ExcelIgnore 
用在属性上,默认所有字段都会和excel去匹配,加了这个注解会忽略该字段

@ExcelIgnoreUnannotated 
默认不管加不加ExcelProperty的注解的所有字段都会参与读写,
加了ExcelIgnoreUnannotated注解以后,不加ExcelProperty注解的字段就不会参与。

➢ 字段值格式化

@DateTimeFormat
时间格式转化,需配合转换器使用

@DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒")
@ExcelProperty(value = {"日期标题"}, converter = DateStringConverter.class)
private Date createTime;

@NumberFormat
用于数字的格式化,其值参考DecimalFormat的规则

@ExcelProperty(value = {"数字标题"})
@NumberFormat("0.0")
private double total;

DecimalFormat pattern:
n = 表达式0/#个数
m = 目标值小数点前位数
l = 目标值小数点后位数

0	每个0表示一个数字,m > n,不做处理;m < n,则用0补齐
					l > n,保留表达式0个数;l < n,则用0补齐
#	每个#表示一个数字,m > n,不做处理;m < n,不做处理
					l > n,保留表达式0个数;l < n,不做处理
.	小数点
,	分组分隔符,###,### ,按后面的#个数进行分组
E	指数表示的底数指数分隔符
%	乘以100并显示为百分比
?	乘以1000并显示为千分比

➢ 写出excel样式设置

@ColumnWidth 列宽,可用在属性和方法上

参数:
value 列高,-1代表自动

@ContentFontStyle 文本字体样式

参数:
fontName 字体名称
fontHeightInPoints 字体高度
italic 是否斜体
strikeout 是否设置删除水平线
color 字体颜色
typeOffset 偏移量
underline 下划线
bold 是否加粗
charset 编码格式

@ContentLoopMerge 文本循环合并

参数:
eachRow 当前属性扩展行,每几行合并一次
columnExtend 当前属性扩展列,向后扩展几列进行合并

@ContentRowHeight 文本行高度

参数:
value 行高,-1代表自动行高

@ContentStyle 文本样式

参数:
dataFormat 日期格式
hidden 设置单元格使用此样式隐藏
locked 设置单元格使用此样式锁定
quotePrefix 在单元格前面增加`符号,数字或公式将以字符串形式展示
horizontalAlignment 设置是否水平居中
wrapped 设置文本是否应换行。将此标志设置为true通过在多行上显示使单元格中的所有内容可见
verticalAlignment 设置是否垂直居中
rotation 设置单元格中文本旋转角度。03版本的Excel旋转角度区间为-90°90°,07版本的Excel旋转角度区间为0°180°
indent 设置单元格中缩进文本的空格数
borderLeft 设置左边框的样式
borderRight 设置右边框样式
borderTop 设置上边框样式
borderBottom 设置下边框样式
leftBorderColor 设置左边框颜色
rightBorderColor 设置右边框颜色
topBorderColor 设置上边框颜色
bottomBorderColor 设置下边框颜色
fillPatternType 设置填充类型
fillBackgroundColor 设置背景色
fillForegroundColor 设置前景色
shrinkToFit 设置自动单元格自动大小

@HeadFontStyle 标题字体样式

参数:
fontName 设置字体名称
fontHeightInPoints 设置字体高度
italic 设置字体是否斜体
strikeout 是否设置删除线
color 设置字体颜色
typeOffset 设置偏移量
underline 设置下划线
charset 设置字体编码
bold 设置字体是否加粗

@HeadRowHeight 标题高度

参数:
value 设置行高,-1代表自动行高

@HeadStyle 标题样式

参数:
dataFormat 日期格式
hidden 设置单元格使用此样式隐藏
locked 设置单元格使用此样式锁定
quotePrefix 在单元格前面增加`符号,数字或公式将以字符串形式展示
horizontalAlignment 设置是否水平居中
wrapped 设置文本是否应换行。将此标志设置为true通过在多行上显示使单元格中的所有内容可见
verticalAlignment 设置是否垂直居中
rotation 设置单元格中文本旋转角度。03版本的Excel旋转角度区间为-90°90°,07版本的Excel旋转角度区间为0°180°
indent 设置单元格中缩进文本的空格数
borderLeft 设置左边框的样式
borderRight 设置右边框样式
borderTop 设置上边框样式
borderBottom 设置下边框样式
leftBorderColor 设置左边框颜色
rightBorderColor 设置右边框颜色
topBorderColor 设置上边框颜色
bottomBorderColor 设置下边框颜色
fillPatternType 设置填充类型
fillBackgroundColor 设置背景色
fillForegroundColor 设置前景色
shrinkToFit 设置自动单元格自动大小

@OnceAbsoluteMerge 合并单元格一次

参数:
firstRowIndex 合并的第一行
lastRowIndex 合并的最后一行
firstColumnIndex 合并的第一列
lastColumnIndex 合并的最后一行

➢ 实体demo

/**
 * 角色信息表-实体
 *
 * @author: chengjiang
 */
@Data
@HeadRowHeight(value = 30)
@ContentRowHeight(value = 25)
@ColumnWidth(value = 25)
@ContentFontStyle(fontName = "宋体", fontHeightInPoints = 15)
@HeadFontStyle(fontName = "宋体", fontHeightInPoints = 18, bold = BooleanEnum.TRUE)
public class SysRoleExcelVO {

    /** 
     * 角色名称 
     */
    @ExcelProperty(value = "角色名称")
    private String roleName;
    
    /** 
     * 角色权限字符串 
     */
    @ExcelProperty(value = "角色名称")
    private String roleKey;

    /** 
     * 角色状态(0正常 1停用)
     */
    @ExcelProperty(value = "角色状态")
    @ExcelIgnore
    private String status;

    /**
     * 创建时间
     */
    @ExcelProperty(value = "创建时间", converter = LocalDateTimeStringConverter.class)
    @DateTimeFormat(value = DateUtils.DATE_FORMAT_8)
    private LocalDateTime createTime;

    /**
     * 数字标题
     */
    @ExcelProperty(value = {"数字标题"})
    @NumberFormat("#,###.###")
    private double total;
    
    /**
     * 角色说明
     */
    @ExcelProperty(value = "角色说明")
    private String remark;

}



springboot结合Easyexcel的使用(详细介绍Easyexcel)小白入门到精通 一、为什么用它?      由于apache poi和jxl,excelPOI都有一个严重的问题,就是非常消耗内存,特别处理数据量多时,速度慢并且时有异常发生,所以改用由阿里研发的easyExcel更可靠一些,它的官方建议对于1000行以内的采用原来poi的写法一次读写,但于1000行以上的数据,有用了一行行进行解析的方案,这样避免了内存的溢出。 二、easyExcel主要功能: 1.支持Excel导入与导出,同时支持xls和xlsm,即07版本和03版本(官方建议03版本不要超过2000行)的Excel文件格式。 2.支持pojo注释时,映射成为java实体模型。 3.支持多个sheet,同 立即下载

相关推荐

(实测可用)STM32L431RCT6开发板-实验1 跑马灯实验

一、开发板平台简介: 1、开发板资源简介 (1)开发板主芯片型号:STM32L431RCT6 (2)开发板主芯片封装:LQFP-64_10x10x05P (3)开发板主芯片内核:ARM® Cortex®-M4 (4)开发板主芯片主频:80MHz (5)开发板主芯片Flash大小:256KB (6)开发板主芯片RAM大小:64KB

zhej2014的博客 3167

EasyExcel导出保留到小数点后两位失效

EasyExcel导出保留到小数点后两位失效

CXY_BOY的博客 605

LabVIEW状态机实战:用STM32+DHT11搭建温湿度监测系统(附完整VI源码)

本文详细介绍了如何利用LabVIEW状态机模式与STM32微控制器、DHT11温湿度传感器构建温湿度监测系统。通过硬件选型、状态机设计、LabVIEW程序实现及系统优化等环节,提供了一套完整的解决方案,并附有VI源码,助力开发者快速实现嵌入式温湿度监测应用。

weixin_42549723的博客 519

【EasyExcel】入门学习及快速上手实践复盘

easyexcel实践复盘

weixin_44174678的博客 426

WPS中VBA支持库安装全攻略:从报错到一键解决

VBA作为Office生态中广泛使用的宏语言,是办公自动化的重要基础。WPS表格虽然兼容Excel文件格式,但出于技术路线与授权原因,默认并未集成完整的VBA引擎与开发环境,导致用户打开含宏文件时频繁遇到“找不到工程或库”“未找到VBA支持库”等报错。理解VBA支持库包含引擎与IDE两层结构,并掌握系统位数、版本匹配、安全软件拦截等前置条件,是成功部署的关键。通过可靠的安装源与规范流程完成配置后,即可实现在WPS中运行Excel宏、批量处理数据、自定义函数等自动化场景,显著提升报表处理效率。本文围绕WPS中

weixin_31102231的博客 308

052、联发科Imagiq HyperEngine架构适配:天玑9000/9200的ISP多核并行调度与Tuning Toolkit调优案例

摘要: 项目组在适配联发科天玑9200的Imagiq 890 ISP架构时,遇到多摄预览功耗过高、帧率不稳等问题。通过分析多核调度机制,调整辅核QoS通道、优化AI-ISP协同模式(采用FREERUN+运动补偿),并利用Tuning Toolkit的Pipeline Viewer动态分配负载,最终降低功耗30mA、提升画质。关键经验包括:避免多核带宽冲突、合理配置动态时钟策略,以及统一校准参数版本管理。天玑9000/9200的HyperEngine架构需优先理解多核协同逻辑,再针对性调优,否则易引发场景兼容

zzfmlxs的博客 36

(六)admin-boot项目之全局处理预防xss攻击

这是一个基础的企业级基础后端脚手架项目,主要由springboot为基础搭建,后期整合一些基础插件例如:redis、xxl-job、flowable、minioio、easyexcel、skyWalking、rabbitmq

itboy 574

YOLOv12环境配置,手把手教你使用YOLOv12训练自己的数据集和推理(附YOLOv12网络结构图),全文最详细教程

YOLO 系统尊嘟太卷了吧,YOLOv11 还没完明白,YOLOv12 就重磅来袭,YOLOv12 是由纽约州立大学联合中科院在 2025年2月19 日发布,从 YOLOv12 论文题目我们大概就知道做了那些更新 ,下图是YOLOv12 在 COCO 数据集上的性能表现引入区域注意力机制(area-attention):通过引入十字形窗口自我注意机制,沿水平和垂直条纹计算注意力机制,纵横交错的注意机制。如下图所示,区域注意力采用最多简单的等分方法,将特征地图垂直或水平划分为 L 个区域。

在职AI算法工程师,擅长计算机视觉,YOLO目标检测、分割等,擅长web、pyqt界面可视化,好内容持续更新中,来这里跟大家一起学习,共同进步 6万+

EasyExcel入门

EasyExcel入门学习记录---学习自b站黑马

伏颜的博客 1964

小梅哥Xilinx FPGA学习笔记11——亚稳态

亚稳态就是D触发器输入信号在其数据窗口期内发生变化,导致D触发器的输出进入一段时间的不稳定状态,有可能发生振荡,并最终随机稳定在高电平或者低电平。

m0_51430584的博客 631

YOLOv8融合HAttention:激活更多像素的注意力机制科研实践指南

HAttention通过层级化像素激活与融合的设计,为YOLOv8的像素级特征利用能力带来了质的飞跃。从原理理解到代码实现,再到集成调优,每一步都是科研中“解决像素信息遗漏难题”的具体实践。如果你在实验中发现某类细节或大尺度目标的检测仍有不足,可以尝试结合数据增强(如随机裁剪、多尺度训练)来强化模型对像素特征的学习能力。科研的价值在于不断挖掘技术的潜力,希望这份指南能真正帮你在目标检测的像素级特征利用领域找到新的突破点,祝你在科研探索中持续收获创新与成长。

FJN110的博客 106

POI及EasyExcel学习笔记

结构:HSSF - 提供读写[Microsoft Excel](https://baike.baidu.com/item/Microsoft Excel)格式档案的功能。XSSF - 提供读写MicrosoftExcelOOXML格式档案的功能。HWPF - 提供读写[Microsoft Word](https://baike.baidu.com/item/Microsoft Word)格式档案的功能。HSLF - 提供读写Microsoft PowerPoint格式档案的功能。

深思纵似丁香结,一展芭蕉寸凡心。 990

ADB 安装教程:如何在 Windows、 Linux 上安装 Android Debug Bridge

本文介绍了在Windows和Linux系统上安装ADB(Android调试桥)的方法。对于Windows系统:1)从官网下载ADB工具并解压;2)通过命令提示符验证安装;3)配置环境变量以实现全局使用;4)再次验证全局ADB功能。Linux系统可通过包管理器直接安装(如Ubuntu使用apt,Fedora使用dnf),安装后同样需验证版本信息确认成功。两种系统安装完成后,都能通过"adb version"命令检查安装状态。

qq_53545309的博客 443

EasyExcel学习笔记

EasyExcel是阿里开源的一款Java Excel处理工具,具有内存占用小、读写性能高的特点,适合处理大文件。文章主要介绍了EasyExcel的简单读写操作、批量处理和模板填充、综合案例等现方式。

m0_56064238的博客 1564

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

Java+EasyExcel 打造学习平台视频学习时长统计系统

本文介绍了一个基于Java17和SpringBoot3.2.5构建的在线教育平台视频学习时长统计系统。该系统通过"定时上报+关键节点上报"策略采集学生学习数据,结合快进检测、暂停排除等规则计算有效时长,支持按用户、课程、班级等多维度统计。系统采用Redis缓存热点数据提升查询性能,使用EasyExcel实现大数据量报表导出,并通过"实时统计+定时全量统计"双机制确保数据准确性。

果酱 の 博客 1816

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

EasyExcel导出到多个sheet 学习笔记

EasyExcel导出到多个sheet的基本步骤

骑着猪看大海的博客 887

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

easyexcel 学习

一、需求 我们需要完成的就是这种表格,内容很复杂,可以分未三个块。第一个是标题块,第二个是数学老师详细资料块,第三个是语文老师详细资料块。其次我们需要根据老师的详细信息的列数来决定其上标题的合并单元格数量,保证对齐美观。 二、实现 一般来说,我们使用easyexcel都是定义一个类,然后使用如下语法,就能导出相应的excel了,并且导出的excel中还会有相应的标题,这些标题行都有默认的样式了。另外还能使用注解方法去设置一些自定义样式以及单元合并等。常规应该以及足够应付那些简单的导出业务了。 @Data

jiqiren1994的博客 2945

EasyExcel学习

一、引入 在一个项目系统中,只要运行了,就会产生很多数据,一般会采用分页显示,但是为了方便浏览数据,一般都会做一个功能,导出成excel,而为了添加数据,也会做一个导入excel功能,进行批量导入。而现在在市场有很多这方面的技术,如Apache poi、jxl。但是这两种技术都存在一个问题,就是非常的耗内存,虽然poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但po...

qq_39581763的博客 541

EasyExcel与增强学习:Excel游戏AI

你是否曾想过用Excel作为游戏环境来训练AI?本文将展示如何结合阿里巴巴开源的Excel处理工具EasyExcel与增强学习(Reinforcement Learning, RL)算法,构建一个能够在Excel表格中进行决策的游戏AI。通过这种创新组合,你将学习如何将表格数据转化为智能决策系统,掌握从环境建模到策略优化的完整流程。 读完本文后,你将能够: - 理解如何使用EasyExcel构建...

gitblog_01022的博客 897

学习EasyExcel的基本处理以及AnalysisEventListener的实现

学习EasyExcel的基本处理以及AnalysisEventListener的实现

gys9895的博客 682

EasyExcel 学习笔记

官网案例// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去@Slf4j/*** 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收*//*** 缓存的数据*//*** 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。*/// 这里是demo,所以随便new一个。

qq_56725871的博客 2072

EasyExcel学习及应用:从入门到百万数据导入

在日常开发中,Excel文件的导入和导出是一个常见需求,而传统的Apache POI等工具在处理大数据量时可能会出现性能瓶颈。阿里巴巴的EasyExcel是一款轻量级、高性能的Excel操作工具,支持简单易用的接口和注解配置,适合在Java项目中快速集成。

Dong雨的博客 1643

POI和easyExcel学习

文章目录一、简介1.1 POI1.2 easyExcel1.3 区别二、实验 POI2.1 第一步:导入依赖2.2 第二步:创建03版本的工作薄2.3 第三步:创建07版本的工作薄2.4 第四步: 读取文件流了解计算公式实验 easyExcel第一步:导入依赖第二步:创建实体类第三步:监听器 一、简介 没啥好讲的就是导入导出Excel POI是Apache的,easyExcel是阿里巴巴的 POI把excel内容一口气加入到OOM(内存);easyExcel是一行一行的加载 1.1 POI POI官网

huiguo_的博客 798

【EasyExcel】学习笔记~从读数据到写数据

本文主要说明EasyExcel 读数据~写数据 操作

少年你真的要止步于此嘛。 1303
上一篇: Xsheel下sftp简单命令整理
下一篇: 线程池使用学习总结
star_falling
博客等级 码龄11年 287粉丝 21原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值