Spring Boot encapsulates the universal Excel export tool

Source: blog.csdn.net/qq_35387940/article/details/129062470

As in the title, this gadget does not limit which table you are looking up and what type you are using. I just swipe the shuttle and give you a good meal. I know, this is what many people have thought about, at least I have received a lot of people asking me this similar question. I also told them, but they just don't do it, it's really very simple.

67030386bff1425981136aac082a619c.png

What if you don't do it? I'll do it. Let's not talk too much.

text

The gameplay is very simple. The effect to be achieved:

The class is undefined, User? Student? District? Not sure.

However, the functions we encapsulate must be sufficient to support different classes. We automatically read and traverse the list, and then export the generated file.

What is the core idea?

In fact, we still use the essence of the content format of the csv file, look at these two pictures:

ca90d48f0fb70b032009fef25801b775.png


We want to achieve a universal class export excel 

What is the idea:

① We take out the classes in the set list of uncertain classes.

Reflexively, take out the attribute name inside, and do the splicing of the title name of the first row of the table.

② Splicing content

Because the class is uncertain, then we use reflection to throw all the field attributes of the class as keys into the map, and at the same time throw the value into the value.

In this way, when we stitch the content, we only need to traverse the stitching according to the map.

Recommend an open source and free Spring Boot most complete tutorial:

https://github.com/dyc87112/SpringBoot-Learning

1. Dependence

<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>3.15</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-scratchpad</artifactId>
    <version>3.15</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.69</version>
</dependency>
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.5</version>
</dependency>

2. I have encapsulated the core tool classes and functions

MyCsvFileUtil.java

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @author JCccc
 * @Remark 是我
 */
@Slf4j
public class MyCsvFileUtil {
    public static final String FILE_SUFFIX = ".csv";
    public static final String CSV_DELIMITER = ",";
    public static final String CSV_TAIL = "\r\n";
    protected static final String DATE_STR_FILE_NAME = "yyyyMMddHHmmssSSS";

    /**
     * 将字符串转成csv文件
     */
    public static void createCsvFile(String savePath, String contextStr) throws IOException {

        File file = new File(savePath);
        //创建文件
        file.createNewFile();
        //创建文件输出流
        FileOutputStream fileOutputStream = new FileOutputStream(file);
        //将指定字节写入此文件输出流
        fileOutputStream.write(contextStr.getBytes("gbk"));
        fileOutputStream.flush();
        fileOutputStream.close();
    }

    /**
     * 写文件
     *
     * @param fileName
     * @param content
     */
    public static void writeFile(String fileName, String content) {
        FileOutputStream fos = null;
        OutputStreamWriter writer = null;
        try {
            fos = new FileOutputStream(fileName, true);
            writer = new OutputStreamWriter(fos, "GBK");
            writer.write(content);
            writer.flush();
        } catch (Exception e) {
            log.error("写文件异常|{}", e);
        } finally {
            if (fos != null) {
                IOUtils.closeQuietly(fos);
            }
            if (writer != null) {
                IOUtils.closeQuietly(writer);
            }
        }
    }

    /**
     * 构建文件名称
     * @param dataList
     * @return
     */
    public static String buildCsvFileFileName(List dataList) {
        return dataList.get(0).getClass().getSimpleName() + new SimpleDateFormat(DATE_STR_FILE_NAME).format(new Date()) + FILE_SUFFIX;
    }

    /**
     * 构建excel 标题行名
     * @param dataList
     * @return
     */
    public static String buildCsvFileTableNames(List dataList) {
        Map<String, Object> map = toMap(dataList.get(0));
        StringBuilder tableNames = new StringBuilder();
        for (String key : map.keySet()) {
            tableNames.append(key).append(MyCsvFileUtil.CSV_DELIMITER);
        }
        return tableNames.append(MyCsvFileUtil.CSV_TAIL).toString();
    }

    /**
     * 构建excel内容
     * @param dataLists
     * @return
     */
    public static String buildCsvFileBodyMap(List dataLists) {
        //不管你传什么玩意,我都给你反射一手,搞成Map
        List<Map<String, Object>> mapList = new ArrayList<>();
        for (Object o : dataLists) {
            mapList.add(toMap(o));
        }
        //然后利用csv格式,对着map嘎嘎一顿拼接数据
        StringBuilder lineBuilder = new StringBuilder();
        for (Map<String, Object> rowData : mapList) {
            for (String key : rowData.keySet()) {
                Object value = rowData.get(key);
                if (Objects.nonNull(value)) {
                    lineBuilder.append(value).append(MyCsvFileUtil.CSV_DELIMITER);
                } else {
                    lineBuilder.append("--").append(MyCsvFileUtil.CSV_DELIMITER);
                }
            }
            lineBuilder.append(MyCsvFileUtil.CSV_TAIL);
        }
        return lineBuilder.toString();
    }

    /**
     * 类转map
     * @param entity
     * @param <T>
     * @return
     */
    public static<T> Map<String, Object> toMap(T entity){
        Class<? extends Object> bean = entity.getClass();
        Field[] fields = bean.getDeclaredFields();
        Map<String, Object> map = new HashMap<>(fields.length);
        for(Field field:fields){
            try {
                if(!"serialVersionUID".equals(field.getName())){
                    String methodName = "get"+field.getName().substring(0, 1).toUpperCase()+field.getName().substring(1);
                    Method method = bean.getDeclaredMethod(methodName);
                    Object fieldValue = method.invoke(entity);
                    map.put(field.getName(),fieldValue);
                }
            } catch (Exception e) {
                log.warn("toMap() Exception={}",e.getMessage());
            }
        }
        return map;
    }
}

Code attention points (various small packages):

① class to map

a63435ad0e9da50b62f23a534e4000bd.png

② Reflection to map, take the field attribute name and stitch the title

ade1c9f399b76da825a4bb07e64bc054.png

③ For list<不确定类>conversion list<map>, and then splicing excel content

480b40ef5d0abeee1ae520cd46c9af4e.png

Test code:

@RequestMapping("/createCsvFileJcTest")
public void createCsvFileJcTest() {
    //类不确定 随便怎么传都行
    List<District> districts = districtService.queryByParentCodes(Arrays.asList("110100"));
    //存放地址&文件名
    String fileName = "D:\\mycsv\\"+MyCsvFileUtil.buildCsvFileFileName(districts);
    //创建表格行标题
    String tableNames = MyCsvFileUtil.buildCsvFileTableNames(districts);
    //创建文件
    MyCsvFileUtil.writeFile(fileName, tableNames);
    //写入数据
    String contentBody = MyCsvFileUtil.buildCsvFileBodyMap(districts);
    //调用方法生成
    MyCsvFileUtil.writeFile(fileName, contentBody);
}

8c3e38c59a5dac6e1236d233d8b6846f.png

Take a look at the effect:

a2b0a164bf64ac5cccdc814cb88204e2.png

Exported excel file content:

d1063848a0576e0201fb1aad461e4a84.png

Next, change the class to play:

5ecb2f88a1b6bc6b13fb90b40ebb0e81.png

Then export to see the effect:

b404f6914b26446479df381134f6b6b6.png

You can see that the data export is also OK:

fd60e62bcff13aa328da199f59a90df6.png

That’s right, it’s as simple as that, and of course it’s a throwback. I hope that after reading this article, you can learn from these reflection function methods and do more fun encapsulation, such as adding some custom annotation analysis, such as adding Some front and rear interceptor extensions and so on.

We have created a high-quality technical exchange group. When you are with excellent people, you will become excellent yourself. Hurry up and click to join the group and enjoy the joy of growing together.


expand

The header exported in the above example is the attribute name. If the formal export usually requires a custom header name, we can use custom annotations to do it here.

JcExcelName.java

/**
 * @Author : JCccc
 * @CreateTime : 2020/5/14
 * @Description :
 **/

@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface JcExcelName {

    String name() default "";

}

Then in the class that you want to export, add the name you want to understand, and take the attribute name if you don’t add it:

50245a3bbfedecc1efc4f9b2c6d9dfd0.png

Write another one casually, the new reflection analysis takes the field attribute annotation value function:

public static <T> List<String> resolveExcelTableName(T entity) {
    List<String> tableNamesList = new ArrayList<>();
    Class<? extends Object> bean = entity.getClass();
    Field[] fields = bean.getDeclaredFields();
    Map<String, Object> map = new HashMap<>(fields.length);
    for (Field field : fields) {
        try {
            if (!"serialVersionUID".equals(field.getName())) {
                String tableTitleName = field.getName();
                JcExcelName myFieldAnn = field.getAnnotation(JcExcelName.class);
                String annName = myFieldAnn.name();
                if (StringUtils.hasLength(annName)) {
                    tableTitleName = annName;
                }
                tableNamesList.add(tableTitleName);
            }
        } catch (Exception e) {
            log.warn("toMap() Exception={}", e.getMessage());
        }
    }
    return tableNamesList;
}

Then splice the table title name format according to the parsed annotation value column name:

public static String buildCsvFileTableNamesNew(List<String> dataList) {
    StringBuilder tableNames = new StringBuilder();
    for (String name : dataList) {
        tableNames.append(name).append(MyCsvFileUtil.CSV_DELIMITER);
    }
    return tableNames.append(MyCsvFileUtil.CSV_TAIL).toString();
}

Test to see the effect:

public static void main(String[] args) {

    User user = new User();
    List<String> nameList = MapUtils.resolveExcelTableName(user);
    System.out.println(nameList.toString());
    String tableNames = buildCsvFileTableNamesNew(nameList);
    System.out.println(tableNames);

}

The effect rattles well:

c04fadd9a947de37d5c7bd8b919d8e36.png

Then backhanded it into the usage example of our previous article:

String tableNames = MyCsvFileUtil.buildCsvFileTableNamesNew( MyCsvFileUtil.resolveExcelTableName(dataList.get(0)));

85ed4620e55151504cd1168dfca4f66a.png

Execute the sample interface to see the effect:

b595f7a3a48c47b1c969b2ef21c80081.png

The file came out:

c3b248684248a0052a9c572d47cd12c7.png

Open it to see the effect:

a27250d0acdd48bfd2d8f0c057f1e3ab.png

Alright, here we go, it's perfect.

------

We have created a high-quality technical exchange group. When you are with excellent people, you will become excellent yourself. Hurry up and click to join the group and enjoy the joy of growing together. In addition, if you want to change jobs recently, I spent 2 weeks a year ago collecting a wave of face-to-face experience from big factories. If you plan to change jobs after the festival, you can click here to claim it !

recommended reading

··································

Hello, I am DD, a programmer. I have been developing a veteran driver for 10 years, MVP of Alibaba Cloud, TVP of Tencent Cloud. From general development to architect to partner. Along the way, my deepest feeling is that we must keep learning and pay attention to the frontier. As long as you can persevere, think more, complain less, and work hard, it will be easy to overtake on corners! So don't ask me if it's too late to do what I do now. If you are optimistic about something, you must persevere to see hope, not to persevere only when you see hope. Believe me, as long as you stick to it, you will be better than now! If you have no direction yet, you can follow me first, and I will often share some cutting-edge information here to help you accumulate capital for cornering and overtaking.

Guess you like

Origin blog.csdn.net/j3T9Z7H/article/details/130998326