Springboot exports EXCEl method (if by instance)

This article introduces the method of exporting the data displayed on the front end as an EXCEL table file in springboot. The method comes from Ruoyi Ruoyi

content

1. Related configuration

1.1 Annotations for configuration

1.2 Add fields

1.3 Add annotations

2. Excel method class

Export method roadmap​

2.1 Define variable fields

2.2 Controller layer

2.3 exportExcel(List list, String sheetName)方法

2.3.1 init method (initialization)

2.3.2 Calling the creteExcelField() method

2.3.3 createWorkbook()

2.4 exportExcel()


Export method roadmap

1. Related configuration

1.1 Introducing the environment

        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
        </dependency>

There are many configurations when exporting data to EXCEL files, such as row height, date format, export type, etc. In order to achieve code reuse and functional diversity, annotation methods should be used for different entity classes, different Data type for individual configuration.

1.2 Annotations for configuration

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Excel
{
   
   

@Rentention is used to define how the annotation is maintained, that is, the life cycle

RetentionPolicy.SOURCE Annotations are only retained in source files, and when Java files are compiled into class files, annotations are discarded;
RetentionPolicy.CLASS The annotations are kept in the class file, but are abandoned when the JVM loads the class file, which is the default life cycle;
RetentionPolicy.RUNTIME The annotation is not only saved in the class file, but still exists after the JVM loads the class file;

@Target is used to describe the scope of use of the annotation, where the described annotation can be used, parameters

1.3 Add fields

The configuration required for the EXCEL table

E.g:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Excel
{
   
     /**
     * 导出时在excel中排序
     */
    public int sort() default Integer.MAX_VALUE;

    /**
     * 导出到Excel中的名字.
     */
    public String name() default "";

    /**
     * 日期格式, 如: yyyy-MM-dd
     */
    public String dateFormat() default "";

    /**
     * 如果是字典类型,请设置字典的type值 (如: sys_user_sex)
     */
    public String dictType() default "";

    /**
     * 读取内容转表达式 (如: 0=男,1=女,2=未知)
     */
    public String readConverterExp() default "";

    /**
     * 分隔符,读取字符串组内容
     */
    public String separator() default ",";

    /**
     * BigDecimal 精度 默认:-1(默认不开启BigDecimal格式化)
     */
    public int scale() default -1;

    /**
     * BigDecimal 舍入规则 默认:BigDecimal.ROUND_HALF_EVEN
     */
    public int roundingMode() default BigDecimal.ROUND_HALF_EVEN;

    /**
     * 导出类型(0数字 1字符串)
     */
    public ColumnType cellType() default ColumnType.STRING;

    /**
     * 导出时在excel中每个列的高度 单位为字符
     */
    public double height() default 14;

    /**
     * 导出时在excel中每个列的宽 单位为字符
     */
    public double width() default 16;

    /**
     * 文字后缀,如% 90 变成90%
     */
    public String suffix() default "";

    /**
     * 当值为空时,字段的默认值
     */
    public String defaultValue() default "";

    /**
     * 提示信息
     */
    public String prompt() default "";

    /**
     * 设置只能选择不能输入的列内容.
     */
    public String[] combo() default {};

    /**
     * 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写.
     */
    public boolean isExport() default true;

    /**
     * 另一个类中的属性名称,支持多级获取,以小数点隔开
     */
    public String targetAttr() default "";

    /**
     * 是否自动统计数据,在最后追加一行统计数据总和
     */
    public boolean isStatistics() default false;

    /**
     * 导出字段对齐方式(0:默认;1:靠左;2:居中;3:靠右)
     */
    Align align() default Align.AUTO;

    public enum Align
    {
        AUTO(0), LEFT(1), CENTER(2), RIGHT(3);
        private final int value;

        Align(int value)
        {
            this.value = value;
        }

        public int value()
        {
            return this.value;
        }
    }

    /**
     * 字段类型(0:导出导入;1:仅导出;2:仅导入)
     */
    Type type() default Type.ALL;

    public enum Type
    {
        ALL(0), EXPORT(1), IMPORT(2);
        private final int value;

        Type(int value)
        {
            this.value = value;
        }

        public int value()
        {
            return this.value;
        }
    }

    public enum ColumnType
    {
        NUMERIC(0), STRING(1), IMAGE(2);
        private final int value;

        ColumnType(int value)
        {
            this.value = value;
        }

        public int value()
        {
            return this.value;
        }
    }

The specific configuration is determined according to your own needs, and the enumeration type is used to judge the usage method later.

1.4 Add annotations

Then you should add annotations to the fields of the entity classes that need to be exported

E.g:

    /** 订单编号 */
    @Excel(name = "订单编号")
    private String orderId;

    /** 客户编号 */
    @Excel(name = "客户编号")
    private Long customerId;

    /** 姓名 */
    @Excel(name = "姓名")
    private String Person;

    @JsonFormat(pattern = "yyyy-MM-dd")
    @Excel(name = "时间", width = 30, dateFormat = "yyyy-MM-dd")
    private Date Time;

2. Excel method class

Export method roadmap

2.1 Define variable fields

The overall configuration of the table should be added in this class

/**
 * Excel相关处理
 * 
 * @author ruoyi
 */
public class ExcelUtil<T>
{
    private static final Logger log = LoggerFactory.getLogger(ExcelUtil.class);

    /**
     * Excel sheet最大行数,默认65536
     */
    public static final int sheetSize = 65536;

    /**
     * 工作表名称
     */
    private String sheetName;

    /**
     * 导出类型(EXPORT:导出数据;IMPORT:导入模板)
     */
    private Type type;

    /**
     * 工作薄对象
     */
    private Workbook wb;

    /**
     * 工作表对象
     */
    private Sheet sheet;

    /**
     * 样式列表
     */
    private Map<String, CellStyle> styles;

    /**
     * 导入导出数据列表
     */
    private List<T> list;

    /**
     * 注解列表
     */
    private List<Object[]> fields;

    /**
     * 最大高度
     */
    private short maxHeight;

    /**
     * 统计列表
     */
    private Map<Integer, Double> statistics = new HashMap<Integer, Double>();
    
    /**
     * 数字格式
     */
    private static final DecimalFormat DOUBLE_FORMAT = new DecimalFormat("######0.00");
    
    /**
     * 实体对象
     */
    public Class<T> clazz;

    public ExcelUtil(Class<T> clazz)
    {
        this.clazz = clazz;
    }

Difference between workbook and payroll:

After creating a new excel, the system defaults to a workbook. In this book, there are generally three worksheets, sheet1, sheet2, and sheet3. It's that simple. A workbook can contain up to 255 worksheets, and a sheet can have up to 65536 rows

2.2 Controller layer

In the controller layer, you need to query the data first, then initialize ExcelUtil and call the method

List<Order> list=service.selectOrder(ids);  

ExcelUtil<Order> util = new ExcelUtil<Order>(Order.class);
 
return util.exportExcel(list, "Excel数据表");

2.3 exportExcel(List<T> list, String sheetName)方法

Enter into the exportExcel(List<T> list, String sheetName) method

/**
     * 对list数据源将其里面的数据导入到excel表单
     * 
     * @param list 导出数据集合
     * @param sheetName 工作表的名称
     * @return 结果
     */
    public AjaxResult exportExcel(List<T> list, String sheetName)
    {
        this.init(list, sheetName, Type.EXPORT);
        return exportExcel();
    }

2.3.1 init method (initialization)

As you can see, an init method (initialization) will be called first, and Type.EXPORT is passed in as export data

public void init(List<T> list, String sheetName, Type type)
    {
        if (list == null)
        {
            list = new ArrayList<T>();
        }
        this.list = list;
        this.sheetName = sheetName;
        this.type = type;
        createExcelField();
        createWorkbook();
    }

The logic is to assign the required exported data and file name, as well as the Type that distinguishes between export and import, to the variable.

2.3.2 Calling the creteExcelField() method

overall code

    /**
     * 得到所有定义字段
     */
    private void createExcelField()
    {   this.fields = new ArrayList<Object[]>();
        List<Field> tempFields = new ArrayList<>();
        tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields()));
        tempFields.addAll(Arrays.asList(clazz.getDeclaredFields()));
        for (Field field : tempFields)
        {
            // 单注解
            if (field.isAnnotationPresent(Excel.class))
            {
                putToField(field, field.getAnnotation(Excel.class));
            }

            // 多注解
            if (field.isAnnotationPresent(Excels.class))
            {
                Excels attrs = field.getAnnotation(Excels.class);
                Excel[] excels = attrs.value();
                for (Excel excel : excels)
                {
                    putToField(field, excel);
                }
            }
        }
        this.fields = this.fields.stream().sorted(Comparator.comparing(objects -> ((Excel) objects[1]).sort())).collect(Collectors.toList());
        this.maxHeight = getRowHeight();
    }

Line by line introduction

/**
         * fields字段定义为存储所有注解的字段,为Object[]的数组
         * */
        this.fields = new ArrayList<Object[]>();

        List<Field> tempFields = new ArrayList<>();
/**
 * clazz字段存储的是实体类字段
* clazz.getSuperclass()为得到实体类的父类方法
* getDeclaredFields()为反射中的方法,获得某个类的所有声明的字段,即包括public,private            
* 和proteced,
* 但是不包括父类的申明字段,所以先获取了父类的声明字段,再获取当前类的声明字段
*
* Array.List()是将数组转化成List集合的方法,用此方法得到的List的长度是不可改变的,
*/

tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields()));
tempFields.addAll(Arrays.asList(clazz.getDeclaredFields()));
 /**
         *遍历得到的所有字段
         */
        for (Field field : tempFields)
        {
            // 单注解
            /**
             * isAnnotationPresent() 判断该字段是否标注该注解
             */
            if (field.isAnnotationPresent(Excel.class))
            {
                /**
                 * 如果标注有@Excel注解,则调用该方法
                 */
                putToField(field, field.getAnnotation(Excel.class));
            }

            // 多注解
            /**
             * 如果字段标注有多个注解,则会进入循环
             */
            if (field.isAnnotationPresent(Excels.class))
            {
                Excels attrs = field.getAnnotation(Excels.class);
                Excel[] excels = attrs.value();
                for (Excel excel : excels)
                {
                    putToField(field, excel);
                }
            }
        }
  /**
         * 根据注解中定义的顺序int大小来排序,默认为Integer.MAX_VALUE;
         */
        this.fields = this.fields.stream().sorted(Comparator.comparing(objects -> ((Excel) objects[1]).sort())).collect(Collectors.toList());

        this.maxHeight = getRowHeight();

which calls the  putToField() method

Put the sum of the fields and annotation parameters obtained by the loop if in fileds<Object[]>, Object[0] is the field, and Object[1] is the annotation parameter

/**
     * 放到字段集合中
     */
    private void putToField(Field field, Excel attr)
    {
        if (attr != null && (attr.type() == Type.ALL || attr.type() == type))
        {
            this.fields.add(new Object[] { field, attr });
        }
    }

and getRowHeight() method

Compare the height values ​​in the annotation parameters one by one, and take the maximum value as the final row height

 /**
     * 根据注解获取最大行高
     */
    public short getRowHeight()
    {
        double maxHeight = 0;
        for (Object[] os : this.fields)
        {
            Excel excel = (Excel) os[1];
            maxHeight = maxHeight > excel.height() ? maxHeight : excel.height();
        }
        return (short) (maxHeight * 20);
    }

2.3.3 createWorkbook()

The last step of the inti method, call the createWorkbook() method

  /**
     * 创建一个工作簿
     */
    public void createWorkbook()
    {
        this.wb = new SXSSFWorkbook(500);
    }

2.4 exportExcel()

View the roadmap

After calling the init method, call the exportExcel method without parameters

    /**
     * 对list数据源将其里面的数据导入到excel表单
     *
     * @return 结果
     */
    public AjaxResult exportExcel()
    {
        OutputStream out = null;
        try
        {
            // 取出一共有多少个sheet.
            double sheetNo = Math.ceil(list.size() / sheetSize);
            for (int index = 0; index <= sheetNo; index++)
            {
                createSheet(sheetNo, index);

                // 产生一行
                Row row = sheet.createRow(0);
                int column = 0;
                // 写入各个字段的列头名称
                for (Object[] os : fields)
                {
                    Excel excel = (Excel) os[1];
                    this.createCell(excel, row, column++);
                }
                if (Type.EXPORT.equals(type))
                {
                    fillExcelData(index, row);
                    addStatisticsRow();
                }
            }
            String filename = encodingFilename(sheetName);
            out = new FileOutputStream(getAbsoluteFile(filename));
            wb.write(out);
            return AjaxResult.success(filename);
        }
        catch (Exception e)
        {
            log.error("导出Excel异常{}", e.getMessage());
            throw new CustomException("导出Excel失败,请联系网站管理员!");
        }
        finally
        {
            if (wb != null)
            {
                try
                {
                    wb.close();
                }
                catch (IOException e1)
                {
                    e1.printStackTrace();
                }
            }
            if (out != null)
            {
                try
                {
                    out.close();
                }
                catch (IOException e1)
                {
                    e1.printStackTrace();
                }
            }
        }
    }

Guess you like

Origin blog.csdn.net/xsj5211314/article/details/123917816