重庆移动网站建设,建立一个网页需要多少钱,广告关键词有哪些,seo外链推广员文章目录 前言正文一、项目简介二、核心代码2.1 pom.xml 依赖配置2.2 ExcelHeadMapFactory2.3 ExcelDataLinkedHashMap2.4 自定义注解 ExcelExportBean2.5 自定义注解 ExcelColumnTitle2.6 建造器接口 Builder2.7 表格工具类 ExcelUtils2.8 GsonUtil2.9 模版类 ExportDynamicCo… 文章目录 前言正文一、项目简介二、核心代码2.1 pom.xml 依赖配置2.2 ExcelHeadMapFactory2.3 ExcelDataLinkedHashMap2.4 自定义注解 ExcelExportBean2.5 自定义注解 ExcelColumnTitle2.6 建造器接口 Builder2.7 表格工具类 ExcelUtils2.8 GsonUtil2.9 模版类 ExportDynamicColumnTemplate2.10 模版建造器 ExportDynamicColumnTemplateBuilder 三、控制器调试Bean的定义3.1 StudentDemo3.2 NameAndFactoryDemo3.3 控制器 ExcelDemoController 四、启动类五、测试 前言
关于万能导出前一阵子写过一个功能是实现了的。 就是在使用时感觉如果需要导出的页面比较多那就会出现比较多的重复代码。就想着优化简化一下能够更方便的使用。 原版代码仓库和这一版的代码仓库相同 https://gitee.com/fengsoshuai/excel-demo 原版中额外增加了转换器枚举转换等功能但是总觉得会让整体功能变复杂所以在这一版中就去掉了。如果有兴趣看看的话可以切换到master分支查看。
简化升级的这一版的代码分支是simple-dynamic-column-export
正文
一、项目简介
本次简化升级本着对使用者友好的目的去实现的。 另外本文会粘贴全部代码 在使用上的简化体现在真正导出时只需要几行代码就能实现功能。可以让你更加专注于业务参数的组装。而且代码比较简洁。 举个例子
GetMapping(/exportDy)
public String exportDy(RequestParam(table) ListString table, HttpServletResponse response) throws IOException {// 构造导出模版ExportDynamicColumnTemplateStudentDemo dynamicColumnTemplate new ExportDynamicColumnTemplateBuilder(StudentDemo.class).columnNameList(table).build();// 制造假数据dynamicColumnTemplate.appendExportDataList(studentDemos());// 转换为excel的字节数组byte[] bytes dynamicColumnTemplate.toExcelByteArray();// 响应到webString fileName System.currentTimeMillis() .xlsx;response.setHeader(Content-disposition, attachment;filename fileName);response.setContentType(application/x-msdownload);response.setCharacterEncoding(utf-8);response.getOutputStream().write(bytes);response.getOutputStream().flush();return success;
}简化后的代码只需要3步操作
根据导出bean使用建造器生成模版实例给模版实例中填充业务数据填充完数据后将数据转换为excel格式的字节数组
当我们抡完这三板斧之后剩下的就是将字节数组响应到web导出。
二、核心代码
2.1 pom.xml 依赖配置
dependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactIdversion2.2.0.RELEASE/version/dependencydependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactIdversion1.18.2/version/dependencydependencygroupIdcom.alibaba/groupIdartifactIdeasyexcel/artifactIdversion2.2.11/versionexclusionsexclusiongroupIdorg.slf4j/groupIdartifactIdslf4j-api/artifactId/exclusion/exclusions/dependency!-- https://mvnrepository.com/artifact/com.google.code.gson/gson --dependencygroupIdcom.google.code.gson/groupIdartifactIdgson/artifactIdversion2.10.1/version/dependency
/dependencies2.2 ExcelHeadMapFactory
表格头映射工厂提供了注册表格头和获取表格头配置的静态方法。
package org.feng.export.factory;import org.feng.export.system.ExcelColumnTitle;
import org.feng.export.system.ExcelExportBean;import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;/*** 表格头映射工厂** author feng*/
public class ExcelHeadMapFactory {/*** 全局表头名映射key内部变量的变量名和中文名映射为value*/private static final MapClass?, MapString, String HEAD_NAME_MAP new HashMap();public static void addHeadClass(Class? headClass) {if (!headClass.isAnnotationPresent(ExcelExportBean.class)) {throw new RuntimeException(HeadClass必须使用注解ExcelExportBean);}HEAD_NAME_MAP.put(headClass, mapToPrepareHead(headClass));}public static MapString, String getHeadMap(Class? headClass) {return HEAD_NAME_MAP.get(headClass);}public static boolean containsHeadClass(Class? headClass) {return HEAD_NAME_MAP.containsKey(headClass);}private static MapString, String mapToPrepareHead(Class? excelHeadClass) {MapString, String namedMap new HashMap();Field[] declaredFields excelHeadClass.getDeclaredFields();for (Field declaredField : declaredFields) {boolean annotationPresent declaredField.isAnnotationPresent(ExcelColumnTitle.class);if (annotationPresent) {ExcelColumnTitle excelProperty declaredField.getAnnotation(ExcelColumnTitle.class);String chineseFieldName excelProperty.value();// 保存字段名和中文变量名namedMap.put(declaredField.getName(), chineseFieldName);}}return namedMap;}
}
2.3 ExcelDataLinkedHashMap
自定义LinkedHashMap用于实现字段的顺序以及动态字段展示。 只提供put 和 putAll方法的实现。想要别的put方法的话读者请自行实现。
package org.feng.export.system;import org.springframework.util.CollectionUtils;import java.util.*;
import java.util.stream.Collectors;/*** 表格数据专用的map带顺序而且初始化的时候依据指定的表头变量字段名确定导出数据的顺序** author feng*/
public class ExcelDataLinkedHashMap extends LinkedHashMapString, Object {private static final long serialVersionUID -8554095999151235982L;/*** 头部字段名缓存*/private final SetString headColumnNamesCache;/*** 指定被忽略的列名*/private final SetString ignoreColumnSet;/*** ExcelDataLinkedHashMap构造器** param headColumnNames 表头字段变量名例如[name,studentNo,age,className]*/public ExcelDataLinkedHashMap(ListObject headColumnNames, String... ignoreColumns) {if (ignoreColumns ! null ignoreColumns.length 0) {ignoreColumnSet new HashSet(Arrays.asList(ignoreColumns));} else {ignoreColumnSet Collections.emptySet();}// 字段名去重ListString headColumnStringNames headColumnNames.stream().distinct().map(Object::toString).collect(Collectors.toList());// 构建字段名缓存this.headColumnNamesCache new HashSet(headColumnStringNames);// 指定列数据排列顺序for (String headColumnName : headColumnStringNames) {this.put(headColumnName, null);}}Overridepublic Object put(String key, Object value) {// 只保存字段名缓存中的key以及valueif (headColumnNamesCache.contains(key)) {// 设置了被忽略的列进行判断处理匹配到了就不保存if (!CollectionUtils.isEmpty(ignoreColumnSet) ignoreColumnSet.contains(key)) {return null;}return super.put(key, value);}return null;}Overridepublic void putAll(Map? extends String, ? map) {map.forEach(this::put);}
}
2.4 自定义注解 ExcelExportBean
package org.feng.export.system;import java.lang.annotation.*;/*** 指定表格导出的bean** author feng*/
Documented
Target({ElementType.TYPE})
Retention(RetentionPolicy.RUNTIME)
public interface ExcelExportBean {
}
2.5 自定义注解 ExcelColumnTitle
package org.feng.export.system;import java.lang.annotation.*;/*** 列名标题注解标注列的标题** author feng*/
Documented
Target({ElementType.FIELD})
Retention(RetentionPolicy.RUNTIME)
public interface ExcelColumnTitle {String value();
}
2.6 建造器接口 Builder
package org.feng.export.util;/*** 建造器接口** author feng*/
public interface BuilderT {T build();
}
2.7 表格工具类 ExcelUtils
提供将数据写入表格的静态方法。
package org.feng.export.util;import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.metadata.WriteSheet;import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;/*** excel工具类** author feng*/
public class ExcelUtils {/*** 导出数据单sheet** param exportData key 是sheet名称value是每个sheet里面的数据支持自定义表头*/public static byte[] easyOut(ListMapString, Object exportData) {return easyOut(Sheet, exportData);}/*** 导出数据单sheet** param exportData key 是sheet名称value是每个sheet里面的数据支持自定义表头*/public static byte[] easyOut(String sheetName, ListMapString, Object exportData) {return easyOut(Collections.singletonMap(sheetName, exportData));}/*** 导出数据多sheet** param exportData key 是sheet名称value是每个sheet里面的数据可以自定义*/public static byte[] easyOut(MapString, ListMapString, Object exportData) {// 导出数据ByteArrayOutputStream out new ByteArrayOutputStream();com.alibaba.excel.ExcelWriter excelWriter EasyExcel.write(out).build();int i 0;for (Map.EntryString, ListMapString, Object entry : exportData.entrySet()) {WriteSheet writeSheet EasyExcel.writerSheet(i, entry.getKey()).head(head(entry.getValue().get(0))).build();i;excelWriter.write(data(entry.getValue(), true), writeSheet);}excelWriter.finish();return out.toByteArray();}private static ListListString head(MapString, Object cellData) {ListListString head new ArrayList();for (String key : cellData.keySet()) {head.add(Collections.singletonList(key));}return head;}private static ListListObject data(ListMapString, Object sheetData, boolean skipHead) {ListListObject data new ArrayList();for (int i 0; i sheetData.size(); i) {if (i 0 skipHead) {continue;}data.add(new ArrayList(sheetData.get(i).values()));}return data;}
}
2.8 GsonUtil
gson工具类提供json处理、转换的静态方法。
package org.feng.export.util;import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.google.gson.reflect.TypeToken;import java.util.Map;/*** gson工具类** version v1.0* author: fengjinsong* date: 2023年08月26日 15时00分*/
public class GsonUtil {/*** 不会序列化空字段的Gson对象*/private static final Gson GSON new GsonBuilder().create();/*** 可以序列化空字段的Gson对象*/private static final Gson GSON_WITH_NULL new GsonBuilder().serializeNulls().create();private static final Gson GSON_WITH_DISABLE_HTML_ESCAPING new GsonBuilder().disableHtmlEscaping().create();public static Gson gson() {return GSON;}/*** 转换对象为json字符串不会序列化空字段** param object 目标对象* return 一个json字符串*/public static String toJson(Object object) {return GSON.toJson(object);}/*** 转换对象为json字符串可以序列化空字段** param object 目标对象* return 一个json字符串*/public static String toJsonWithNull(Object object) {return GSON_WITH_NULL.toJson(object);}/*** 转换对象为json字符串禁止html转义** param object 目标对象* return 一个json字符串*/public static String toJsonWithDisableTtmlEscaping(Object object) {return GSON_WITH_DISABLE_HTML_ESCAPING.toJson(object);}public static T T fromJson(String jsonStr, ClassT clazz) {return GSON.fromJson(jsonStr, clazz);}public static MapString, String toStringMap(String jsonStr) {return GSON.fromJson(jsonStr, new TypeTokenMapString, String() {}.getType());}/*** 校验字符串是否是一个json格式* br 注意{code {}} 也是符合条件的json** param jsonStr 目标字符串* return true表示目标是一个正确的json格式*/public static boolean validateJson(String jsonStr) {JsonElement jsonElement;try {jsonElement JsonParser.parseString(jsonStr);} catch (Exception e) {return false;}if (jsonElement null) {return false;}return jsonElement.isJsonObject();}
}
2.9 模版类 ExportDynamicColumnTemplate
package org.feng.export;import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.feng.export.factory.ExcelHeadMapFactory;
import org.feng.export.system.ExcelDataLinkedHashMap;
import org.feng.export.util.ExcelUtils;
import org.feng.export.util.GsonUtil;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;import java.util.*;/*** 导出动态列模版** author feng*/
Slf4j
Getter
public class ExportDynamicColumnTemplateT {/*** 导出的bean类型*/private ClassT exportBean;/*** 导出的数据*/private ListMapString, Object exportData;/*** 当前表头映射*/private MapString, Object currentHeadMap;/*** 真正导出的列名*/private ListObject realExportColumnNameList;/*** 是否转换前打印导出数据(含表头)*/Setterprivate boolean logExportData;/*** 是否转换前打印实际的表头信息*/Setterprivate boolean logCurrentHead;ExportDynamicColumnTemplate(ClassT exportBean, ListString columnNameList) {this(exportBean, columnNameList, false, true);}ExportDynamicColumnTemplate(ClassT exportBean, ListString columnNameList, boolean logExportData, boolean logCurrentHead) {init(exportBean, columnNameList);this.logExportData logExportData;this.logCurrentHead logCurrentHead;}/*** 追加导出数据** param data 数据* param ignoreColumns 设置忽略的列名*/public void appendExportData(T data, String... ignoreColumns) {Objects.requireNonNull(data);ExcelDataLinkedHashMap dataMap new ExcelDataLinkedHashMap(realExportColumnNameList, ignoreColumns);exportData.add(dataMap);String dataJson GsonUtil.toJson(data);dataMap.putAll(GsonUtil.toStringMap(dataJson));}/*** 追加导出数据列表** param dataList 数据列表* param ignoreColumns 设置忽略的列名*/public void appendExportDataList(ListT dataList, String... ignoreColumns) {if (CollectionUtils.isEmpty(dataList)) {return;}dataList.forEach(data - appendExportData(data, ignoreColumns));}/*** 将当前需要导出的数据转换为表格的字节数组** return 字节数组*/public byte[] toExcelByteArray() {return toExcelByteArray(Sheet);}/*** 将当前需要导出的数据转换为表格的字节数组** param sheetName 指定sheet名* return 字节数组*/public byte[] toExcelByteArray(String sheetName) {if (logExportData) {logExportData();}if (logCurrentHead) {log.info(导出数据表头信息{}, currentHeadMap);}return ExcelUtils.easyOut(sheetName, exportData);}private void logExportData() {for (int i 0; i exportData.size(); i) {MapString, Object data exportData.get(i);log.info(导出数据[{}]{}, i, data);}}/*** 初始化导出模版信息主要是表头信息** param exportBean 导出bean的类型* param columnNameList 需要导出的字段名列表*/private void init(ClassT exportBean, ListString columnNameList) {this.exportBean exportBean;exportData new ArrayList();currentHeadMap new LinkedHashMap();// 获取代码配置的表头信息if (!ExcelHeadMapFactory.containsHeadClass(exportBean)) {ExcelHeadMapFactory.addHeadClass(exportBean);}MapString, String headMap ExcelHeadMapFactory.getHeadMap(exportBean);Assert.notEmpty(headMap, 表头不能为空请检查exportBean的类型);// 初始化真正的表头信息过滤无效配置或找不到的列名realExportColumnNameList new ArrayList();for (String field : columnNameList) {String fieldChineseName headMap.get(field);if (StringUtils.isEmpty(fieldChineseName)) {log.info(代码配置的导出表头不完整不存在字段:{}, field);} else {currentHeadMap.put(fieldChineseName, field);realExportColumnNameList.add(field);}}// 记录当前的表头信息exportData.add(currentHeadMap);}
}
2.10 模版建造器 ExportDynamicColumnTemplateBuilder
用于生成模版实例。
package org.feng.export;import lombok.AccessLevel;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.feng.export.system.ExcelExportBean;
import org.feng.export.util.Builder;
import org.springframework.util.Assert;import java.util.List;/*** 导出动态列模版建造器** author feng*/
Accessors(chain true, fluent true)
Setter
public class ExportDynamicColumnTemplateBuilderT implements BuilderExportDynamicColumnTemplateT {/*** 字段名列表*/private ListString columnNameList;/*** 导出的bean类型*/Setter(AccessLevel.NONE)private final ClassT exportBean;/*** 是否转换前打印导出数据(含表头)*/private Boolean logExportData;/*** 是否转换前打印实际的表头信息*/private Boolean logCurrentHead;public ExportDynamicColumnTemplateBuilder(ClassT exportBean) {if (!exportBean.isAnnotationPresent(ExcelExportBean.class)) {throw new RuntimeException(ExportBean必须使用注解ExcelExportBean);}this.exportBean exportBean;}Overridepublic ExportDynamicColumnTemplateT build() {check();ExportDynamicColumnTemplateT template new ExportDynamicColumnTemplate(exportBean, columnNameList);if(logCurrentHead ! null) {template.setLogCurrentHead(logCurrentHead);}if(logExportData ! null) {template.setLogExportData(logExportData);}return template;}private void check() {Assert.notNull(exportBean, 导出的实例类型不能为空);Assert.notEmpty(columnNameList, 字段名列表不能为空);}
}
三、控制器调试Bean的定义
这一部分是非核心代码属于对核心代码使用的一种演示。读者可以按照对应的写法来实现功能。 注意导出的bean定义需要使用注解 ExcelExportBean其中的字段需要使用注解ExcelColumnTitle。 3.1 StudentDemo
package org.feng.headbean;import org.feng.export.system.ExcelColumnTitle;
import lombok.Data;
import org.feng.export.system.ExcelExportBean;/*** 学生demo导出bean** author feng*/
Data
ExcelExportBean
public class StudentDemo {ExcelColumnTitle(姓名)private String name;ExcelColumnTitle(年龄)private String age;ExcelColumnTitle(性别)private String sex;ExcelColumnTitle(学号)private String studentNo;ExcelColumnTitle(班级)private String className;
}
3.2 NameAndFactoryDemo
该类对本次演示无实际意义保留着是因为需要演示加载多个配置表头的实例时的写法。 具体的可以查看启动类ExcelDemoApplication中的内容。
package org.feng.headbean;import org.feng.export.system.ExcelColumnTitle;
import lombok.Data;
import org.feng.export.system.ExcelExportBean;/*** TODO** author feng*/
Data
ExcelExportBean
public class NameAndFactoryDemo {ExcelColumnTitle(名字)private String name;ExcelColumnTitle(工厂)private String factory;
}
3.3 控制器 ExcelDemoController
package org.feng.controller;import org.feng.export.ExportDynamicColumnTemplate;
import org.feng.export.ExportDynamicColumnTemplateBuilder;
import org.feng.headbean.StudentDemo;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;Controller
RequestMapping(/excel)
public class ExcelDemoController {GetMapping(/exportDy)public String exportDy(RequestParam(table) ListString table, HttpServletResponse response) throws IOException {// 构造导出模版ExportDynamicColumnTemplateStudentDemo dynamicColumnTemplate new ExportDynamicColumnTemplateBuilder(StudentDemo.class).columnNameList(table).build();// 制造假数据dynamicColumnTemplate.appendExportDataList(studentDemos());// 转换为excel的字节数组byte[] bytes dynamicColumnTemplate.toExcelByteArray();// 响应到webString fileName System.currentTimeMillis() .xlsx;response.setHeader(Content-disposition, attachment;filename fileName);response.setContentType(application/x-msdownload);response.setCharacterEncoding(utf-8);response.getOutputStream().write(bytes);response.getOutputStream().flush();return success;}private ListStudentDemo studentDemos() {ListStudentDemo studentDemos new ArrayList();for (int i 0; i 5; i) {StudentDemo studentDemo new StudentDemo();studentDemo.setStudentNo(100 - i);studentDemo.setAge(String.valueOf(20 i));studentDemo.setSex(i 2 ? 男 : 女);studentDemo.setClassName(一班);studentDemo.setName(小米 (i1));studentDemos.add(studentDemo);}return studentDemos;}
}
四、启动类
启动项目时加载代码配置信息。
package org.feng;import org.feng.export.factory.ExcelHeadMapFactory;
import org.feng.headbean.NameAndFactoryDemo;
import org.feng.headbean.StudentDemo;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;import java.util.ArrayList;
import java.util.List;SpringBootApplication
public class ExcelDemoApplication implements CommandLineRunner {public static void main(String[] args) {SpringApplication.run(ExcelDemoApplication.class, args);}Overridepublic void run(String... args) throws Exception {ListClass? needRegisterExcelHeadClassList new ArrayList();needRegisterExcelHeadClassList.add(NameAndFactoryDemo.class);needRegisterExcelHeadClassList.add(StudentDemo.class);needRegisterExcelHeadClassList.forEach(ExcelHeadMapFactory::addHeadClass);}
}
五、测试
在谷歌浏览器访问 http://localhost:8080/excel/exportDy?tablename,className,studentNo
会下载得到这样的文件