O Java POI seleciona dinamicamente a coluna do campo exportado

Cenário de negócios: o botão de exportação de uma página, porque há muitos campos e o cliente deseja exportar campos diferentes para facilitar a observação a cada vez, por isso é necessário selecionar dinamicamente a coluna de exportação do Excel

自定义注解 + POI O método adotado neste artigo é usado para realizar
sem mais delongas, vamos ver o efeito primeiro, se não atender o seu efeito, basta excluí-lo.

1. Exibição de renderizações

1.1 transferência de parâmetros front-end analógico carteiro

Os seguintes valores de campo são passados ​​para representar as colunas que precisam ser exportadas
insira a descrição da imagem aqui

1.2 Renderizações Excel exportadas

insira a descrição da imagem aqui

Vamos falar brevemente sobre a ideia e o processo do código:
1. Os campos que precisam ser exportados na transferência de parâmetros front-end e os cabeçalhos do Excel correspondentes aos campos
2. Os campos na primeira etapa do loop, desenhe as células de cada linha de acordo com o conjunto de dados encontrado
3. A chave Etapa 1: Encontre o relacionamento correspondente entre a transmissão de front-end e a participação em nosso conjunto de dados (a lista de entidades de dados encontradas no mysql) e desenhe cada célula de acordo com isso etapa.
Neste artigo, o reflexo da classe de entidade é usado para obter o relacionamento correspondente. Para obter detalhes, consulte o código em "ExportExcel.getValues(Object rowData, String[] exportFieldArr)", o código principal!

2. Exibição de código

2.1. Classe de entidade

2.1.1 dto para receber front-end


/**
 * <p>@Description: 用于接收前端传参的dto</p >
 * <p>@param </p >
 */
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class DynamicExportRequest {
    
    

    //@ApiModelProperty("前端选择的需要导出的字段")
    private GoodsExportBean goodsExportBean;
    // 分页参数
    private int pageIndex;
    private int pageSize;
}

2.1.2 classe de entidade mysql


package com.lzq.learn.test.动态选择列导出excel;

import com.lzq.learn.anno.DynamicExport;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.math.BigDecimal;
import java.time.LocalDateTime;

/**
 * <p>@Description: 实体类</p >
 * <p>@date 18:58 18:58</p >
 * @author 吴巴格
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Goods {
    
    

    private Integer id;

    private String name;

    private BigDecimal price;

    private String type;

    private BigDecimal quantity;

    private LocalDateTime createTime;

}

2.1.3 dto usado para receber campos front-end que precisam ser exportados

package com.lzq.learn.test.动态选择列导出excel.byExportBean;

import com.lzq.learn.anno.DynamicExport;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * <p>@Description: 用于接收前端哪些需要导出字段的dto</p >
 * <p>@date 9:28 9:28</p >
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class GoodsExportBean {
    
    


    @DynamicExport(sort = 1, name = "id")
    private String id;

    @DynamicExport(sort = 2, name = "姓名")
    private String name;

    @DynamicExport(sort = 3, name = "价格")
    private String price;

    @DynamicExport(sort = 4, name = "类型")
    private String type;

    @DynamicExport(sort = 5, name = "库存")
    private String quantity;

    @DynamicExport(sort = 6, name = "创建时间")
    private String createTime;
}

2.1.4 Entidades de campo opcionais para o front end

package com.lzq.learn.test.动态选择列导出excel;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * <p>@Description: 用于返回给前端的可选择导出列 </p >
 * <p>@date 14:23 14:23</p >
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class DynamicField {
    
    

    //("字段英文名")
    private String englishFiledName;

    //("excel显示表头中文名")
    private String chineseTitleName;

    public static DynamicField build(String englishFiledName, String chineseTitleName) {
    
    
        DynamicField dynamicField = new DynamicField();
        dynamicField.setEnglishFiledName(englishFiledName);
        dynamicField.setChineseTitleName(chineseTitleName);
        return dynamicField;
    }
}

2.2 Classes de ferramentas e anotações

2.2.1 Anotações personalizadas

package com.lzq.learn.anno;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
 * <p>@Description: 动态导出字段, 将可选字段加上该注解</p >
 * <p>@date 10:26 10:26</p >
 */
@Target({
    
    TYPE, FIELD, METHOD})
@Retention(RUNTIME)
public @interface DynamicExport {
    
    

    int sort() default 0;

    String name() default "";
}

2.2.2 Classe de ferramenta de coluna de campo dinâmico

package com.lzq.learn.test.动态选择列导出excel;

import com.lzq.learn.anno.DynamicExport;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

/**
 * <p>@Description: 可选择动态字段列工具类</p >
 * <p>@date 14:26 14:26</p >
 */
public class DynamicFieldUtil {
    
    

    /**
     * <p>@Description: 带有 @DynamicExport 注解的实体类,利用反射返回可选择动态列</p >
     * <p>@param [object]</p >
     * <p>@return java.util.List<com.hjza.environment.response.export.DynamicField></p >
     * <p>@throws </p >
     */
    public static <T> List<DynamicField> exportPageListDynamicFieldList(Class<T> objectClass){
    
    
        Field[] declaredFields = objectClass.getDeclaredFields();
        List<DynamicField> dynamicFieldList = new ArrayList<>();
        for (Field declaredField : declaredFields) {
    
    
            declaredField.setAccessible(true);
            DynamicExport dynamicExportAnnotation = declaredField.getAnnotation(DynamicExport.class);
            String chineseTitle = dynamicExportAnnotation.name();
            dynamicFieldList.add(DynamicField.build(declaredField.getName(), chineseTitle));
        }
        return dynamicFieldList;
    }

}

2.2.3 Principais ferramentas de POI

package com.lzq.learn.test.动态选择列导出excel.byExportBean;

import cn.hutool.core.util.StrUtil;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
 * <p>@Description: 将 Apache POI 的一些常用api进行封装</p >
 */
public class ExportExcel {
    
    



    /**
     * <p>@Description: 根据 所选动态列,以及数据集进行导出excel</p >
     * <p>@param [exportBeanFrom, dataList, fileName]</p >
     * <p>@return void</p >
     * <p>@throws </p >
     */
    public static <T> void exportByTitleAndData(Object exportBeanFrom, List<T> dataList, String fileName) {
    
    
        // 处理参数: 需要导出的英文字段名 exportFieldArr, 中文表头 title
        Map<String, Object> beanExportFieldMap = ExportExcelSortUtil.filterAndSort(exportBeanFrom);
        Map<String, String[]> fieldAndTitleMap = ExportExcelSortUtil.getExportFieldAndExportTitle(beanExportFieldMap);
        String[] title = ExportExcelSortUtil.getExportTitleArr(fieldAndTitleMap);
        String[] exportFieldArr = ExportExcelSortUtil.getExportFieldArr(fieldAndTitleMap);
        // 根据参数 生成工作簿,并写入文件流
        if (title.length > 0 && exportFieldArr.length > 0) {
    
    
            Workbook workbook = ExportExcel.getWorkbook(dataList, exportFieldArr, title);
            ExportExcel.writeToResponse(workbook, fileName);
        }
    }

    /**
     * <p>@Description: 将文件流写会回 response 中</p >
     * <p>@param [workbook, fileName]</p >
     * <p>@return void</p >
     * <p>@throws </p >
     */
    public static void writeToResponse(Workbook workbook, String fileName) {
    
    
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletResponse response = attributes.getResponse();
        response.setContentType("application/vnd.ms-excel;charset=UTF-8");
        response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
        OutputStream outputStream = null;
        try {
    
    
            outputStream = response.getOutputStream();
            workbook.write(outputStream);
            outputStream.flush();
            outputStream.close();
        } catch (IOException e) {
    
    
            e.printStackTrace();
            throw new RuntimeException(e.getMessage());
        }
    }


    /**
     * <p>@Description: 根据 数据集、导出列、表头汉字 创建工作簿</p >
     * <p>@param [dataSet 数据集, exportFieldArr 需要导出的字段列, titles 导出列对应的中文表头]</p >
     * <p>@return org.apache.poi.xssf.usermodel.XSSFWorkbook</p >
     * <p>@date 15:19 15:19</p >
     */
    public static <T> XSSFWorkbook getWorkbook(Collection<T> dataSet, String[] exportFieldArr, String[] titles) {
    
    
        // 校验变量和预期输出excel列数是否相同
        if (exportFieldArr.length != titles.length) {
    
    
            return null;
        }
        // 存储每一行的数据
        List<String[]> list = new ArrayList<>();
        for (Object obj : dataSet) {
    
    
            // 获取到每一行的属性值数组
            list.add(getValues(obj, exportFieldArr));
        }
        return getWorkbook(titles, list);
    }

    public static XSSFWorkbook getWorkbook(String[] titles, List<String[]> list) {
    
    
        // 定义表头
        String[] title = titles;
        // 创建excel工作簿
        XSSFWorkbook workbook = new XSSFWorkbook();
        // 创建工作表sheet
        XSSFSheet sheet = workbook.createSheet();
        // 创建第一行
        XSSFRow row = sheet.createRow(0);
        XSSFCell cell = null;
        // 插入第一行数据的表头
        row.setHeight((short) (24 * 20));
        CellStyle headerCommonStyle = getHeaderCommonStyle(workbook);
        for (int i = 0; i < title.length; i++) {
    
    
            cell = row.createCell(i);
            cell.setCellValue(title[i]);
            cell.setCellStyle(headerCommonStyle);
        }
        // 数据行渲染
        CellStyle bodyStyle = getBodyStyle(workbook);
        int idx = 1;
        for (String[] strings : list) {
    
    
            XSSFRow nrow = sheet.createRow(idx++);
            XSSFCell ncell = null;
            for (int i = 0; i < strings.length; i++) {
    
    
                ncell = nrow.createCell(i);
                ncell.setCellValue(strings[i]);
                ncell.setCellStyle(bodyStyle);
            }
        }
        // 设置固定列宽
        setColumnWidth(titles, sheet);
        return workbook;
    }

    // 设置固定列宽
    public static void setColumnWidth(String[] titles, Sheet sheet) {
    
    
        for (int i = 0; i < titles.length; i++) {
    
    
            sheet.setColumnWidth(i, 20 * 256);
        }
    }

    private static CellStyle getHeaderCommonStyle(Workbook workbook) {
    
    
        CellStyle header = workbook.createCellStyle();
        Font font = workbook.createFont();
        font.setBold(Boolean.TRUE);
        font.setFontHeightInPoints((short) 14);
        font.setFontName("宋体");
        header.setFont(font);
        header.setBorderTop(BorderStyle.THIN);
        header.setBorderLeft(BorderStyle.THIN);
        header.setBorderBottom(BorderStyle.THIN);
        header.setBorderRight(BorderStyle.THIN);
        header.setAlignment(HorizontalAlignment.CENTER);
        header.setVerticalAlignment(VerticalAlignment.CENTER);
        header.setFillPattern(FillPatternType.SOLID_FOREGROUND);
//        header.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
        return header;
    }
    private static CellStyle getBodyStyle(Workbook workbook) {
    
    
        CellStyle body = workbook.createCellStyle();
        Font font = workbook.createFont();
        font.setFontHeightInPoints((short) 12);
        font.setFontName("宋体");
        body.setFont(font);
        body.setWrapText(Boolean.TRUE);
        body.setBorderTop(BorderStyle.THIN);
        body.setBorderLeft(BorderStyle.THIN);
        body.setBorderBottom(BorderStyle.THIN);
        body.setBorderRight(BorderStyle.THIN);
        body.setAlignment(HorizontalAlignment.LEFT);
        body.setVerticalAlignment(VerticalAlignment.CENTER);
        body.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        body.setFillForegroundColor(IndexedColors.WHITE.getIndex());
        return body;
    }

    /**
     * <p>@Description: object就是每一行的数据</p >
     * <p>@param [rowData, exportFieldArr]</p >
     * <p>@return java.lang.String[]</p >
     * <p>@throws </p >
     */
    public static String[] getValues(Object rowData, String[] exportFieldArr) {
    
    
        String[] values = new String[exportFieldArr.length];
        try {
    
    
            for (int i = 0; i < exportFieldArr.length; i++) {
    
    

                Field field = null;

                try {
    
    
                    field = rowData.getClass().getDeclaredField(exportFieldArr[i]);
                } catch (Exception e) {
    
    
                    field = rowData.getClass().getField(exportFieldArr[i]);
                }
                // 设置访问权限为true
                field.setAccessible(true);
                values[i] = setCellValue(field, rowData);
            }
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
        return values;
    }

    public static String setCellValue(Field field, Object data) {
    
    
        Class<?> fieldType = field.getType();
        String result = "";
        try {
    
    
            Method method = data.getClass().getMethod(getMethodNameByCamel("get", field.getName()));
            Object fieldValue = method.invoke(data);
            if (fieldType == String.class) {
    
    
                result = (method.invoke(data) == null ? null : method.invoke(data).toString());
            } else if (fieldType == Short.class) {
    
    
                result = returnStringFromNumber(fieldValue);
            } else if (fieldType == Integer.class) {
    
    
                result = returnStringFromNumber(fieldValue);
            } else if (fieldType == Long.class) {
    
    
                result = returnStringFromNumber(fieldValue);
            } else if (fieldType == Float.class) {
    
    
                result = returnStringFromNumber(fieldValue);
            } else if (fieldType == Double.class) {
    
    
                result = returnStringFromNumber(fieldValue);
            } else if (fieldType == BigDecimal.class) {
    
    
                result = returnStringFromNumber(fieldValue);
            } else if (fieldType == Boolean.class) {
    
    
                result = returnStringFromNumber(fieldValue);
            } else if (fieldType == LocalDate.class) {
    
    
                String pattern = "yyyy-MM-dd";
                LocalDate date = method.invoke(data) == null ? null : (LocalDate) method.invoke(data);
                if (date != null) {
    
    
                    result = (date.format(DateTimeFormatter.ofPattern(pattern)));
                }
            } else if (fieldType == LocalDateTime.class) {
    
    
                String pattern = "yyyy-MM-dd HH:mm:ss";
                LocalDateTime date = method.invoke(data) == null ? null : (LocalDateTime) method.invoke(data);
                if (date != null) {
    
    
                    result = (date.format(DateTimeFormatter.ofPattern(pattern)));
                }
            } else {
    
    
                result = (method.invoke(data) == null ? null : method.invoke(data).toString());
            }
            return result;
        } catch (NoSuchMethodException e) {
    
    
            e.printStackTrace();
        } catch (IllegalAccessException e) {
    
    
            e.printStackTrace();
        } catch (InvocationTargetException e) {
    
    
            e.printStackTrace();
        }
        return result;
    }

    // 将数字转换为 保留两位小数的字符串
    public static String returnStringFromNumber(Object data) {
    
    
        if (data == null || StrUtil.isBlank(data.toString())) {
    
    
            return "";
        }
        Double aDouble = Double.valueOf(data.toString());
        DecimalFormat decimalFormat = new DecimalFormat("0.00");
        String result = decimalFormat.format(aDouble);
        return result;
    }


    /**
     * <p>@Description: 拼接前缀以及方法名,驼峰形式</p >
     * <p>@param [prefix, fieldName]</p >
     * <p>@return java.lang.String</p >
     * <p>@throws </p >
     */
    private static String getMethodNameByCamel(String prefix, String fieldName) {
    
    
        StringBuilder builder = new StringBuilder()
                .append(prefix)
                .append(fieldName.substring(0, 1).toUpperCase())
                .append(fieldName.substring(1));
        return builder.toString();
    }
}

2.3 Camada de chamada comercial

2.3.1 controlador



import com.lzq.learn.domain.CommonResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
 * @author 吴巴哥
 */
@RestController
@RequestMapping("/dynamic")
public class TestDynamicExcelController {
    
    


    @Autowired
    private DynamicExportService dynamicExportService;

    // 方式一:通过注解的形式,动态导出excel
    @PostMapping("/exportByFields")
    public void exportByFields(@RequestBody DynamicExportRequest  dynamicExportRequest) {
    
    
        dynamicExportService.exportByFields(dynamicExportRequest , "1.xlsx");
    }


    //("返回给前端的导出Excel的动态列")
    @GetMapping("/getDynamicFields")
    public CommonResult getDynamicFields() {
    
    
        List<DynamicField> dynamicFieldList = dynamicExportService.getDynamicFields();
        return CommonResult.success(dynamicFieldList, "获取成功");
    }

}

2.3.2 código de serviço


import com.lzq.learn.test.动态选择列导出excel.byExportBean.ExportExcel;
import com.lzq.learn.test.动态选择列导出excel.byExportBean.GoodsExportBean;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

@Service
@Slf4j
public class DynamicExportService {
    
    



    // 模拟从 mysql 获取数据
    public List getDataList() {
    
    
        ArrayList<Goods> goodsList = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
    
    
            BigDecimal quantity = BigDecimal.TEN.add(BigDecimal.valueOf(i));
            Goods goods = new Goods(i, "商品"+i, BigDecimal.valueOf(i), "类型"+i, quantity, LocalDateTime.now());
            goodsList.add(goods);
        }
        return goodsList;
    }



    public void exportStaticsByReflect(DynamicExportRequest dynamicExportRequest, String fileName) {
    
    

    }

    public void exportByFields(DynamicExportRequest dynamicExportRequest, String fileName) {
    
    
        List<Goods> dataList = this.getDataList();
        GoodsExportBean goodsExportBean = dynamicExportRequest.getGoodsExportBean();
        ExportExcel.exportByTitleAndData(goodsExportBean, dataList, fileName);
    }

    public List<DynamicField> getDynamicFields() {
    
    
        List<DynamicField> dynamicFieldList = DynamicFieldUtil.exportPageListDynamicFieldList(GoodsExportBean.class);
        return dynamicFieldList;
    }
}

3. Exemplo de chamada de interface do carteiro

3.1 Obter colunas de campo de exportação opcionais

Endereço da interface: localhost:8089/dynamic/getDynamicFields
O valor de retorno da interface é o seguinte

{
    
    
	"code": 200,
	"message": "获取成功",
	"data": [
		{
    
    
			"englishFiledName": "id",
			"chineseTitleName": "id"
		},
		{
    
    
			"englishFiledName": "name",
			"chineseTitleName": "姓名"
		},
		{
    
    
			"englishFiledName": "price",
			"chineseTitleName": "价格"
		},
		{
    
    
			"englishFiledName": "type",
			"chineseTitleName": "类型"
		},
		{
    
    
			"englishFiledName": "quantity",
			"chineseTitleName": "库存"
		},
		{
    
    
			"englishFiledName": "createTime",
			"chineseTitleName": "创建时间"
		}
	]
}

3.2 Exportar Excel após selecionar os campos

Endereço da interface: localhost:8089/dynamic/exportByFields
solicitação de postagem, o formato de passagem de parâmetro é o seguinte

{
    
    
    "goodsExportBean":{
    
    
        //"id":"主键",
        "name":"商品名称",
        "price":"价格",
        //"type":"类型",
        "quantity":"库存哦"
        //"createTime":"上架时间"
    }
}

Finalmente, onde o texto não é atencioso, espero que vocês possam fornecer comentários ativamente, aceitarei com humildade e corrigirei a tempo!

Acho que você gosta

Origin blog.csdn.net/lzq2357639195/article/details/131644196
Recomendado
Clasificación