SpringBoot implements a common export excel file case

The core idea:

       ① From the set list of uncertain classes, take out the classes inside, use the reflection technology to take out the attribute names of the classes, and splicing the title names of the first row of the table.

      ② For splicing content, since the class is uncertain, reflection is used to map all field attributes of the class as keys to the map, and at the same time put the value into value. Finally, when splicing the content, only the map is needed for data splicing.

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. The core tool class: CsvFileUtil.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 splendor.s
 * @date 2023/03/09 14:12 周日
 */
@Slf4j
public class CsvFileUtil {
    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(CsvFileUtil.CSV_DELIMITER);
                } else {
                    lineBuilder.append("--").append(CsvFileUtil.CSV_DELIMITER);
                }
            }
            lineBuilder.append(CsvFileUtil.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;
    }
}

Test code:

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

extension:

       The header exported in the file is the attribute name. If the formal export usually requires a custom header name, custom annotations can be used here to handle it.

JcExcelName.java

@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface JcExcelName {
 
    String name() default "";
 
}

Then add @JcExcelName to the attribute of the class you want to export, otherwise the attribute name will be used as the table header by default:

For example: @JcExcelName(name="user name")

Add a new reflection parsing function to take field attribute annotation value:

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 Case:

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);

}

Guess you like

Origin blog.csdn.net/SHYLOGO/article/details/129417133
Recommended