java_高并发之SpringBoot中实现一个通用Excel导出功能
SpringBoot中实现一个通用Excel导出功能
这个导出功能的特色
- 非常通用,能够满足大部分项目中99%的导出功能,代码复用性很强
- 导出的列可以由前端自定义(比如哪些列需要导出、列的顺序、名称等都是可以由前端自定义)
看效果
先带大家看一下效果。
启动com.pplication
,启动好之后,浏览器中打开`http://localhost:8080/userList`,看效果。
代码解析
com.controller.UserController#userExport
入参(ExcelExportRequest)
public class ExcelExportRequest {
/**
* excel名称
*/
private String excelName;
/**
* sheet的名称
*/
private String sheetName;
/**
* 导出字段有序列表
*/
private List<ExcelExportField> fieldList;
}
出参(ExcelExportResponse)
public class ExcelExportResponse {
//导出的excel文件名称
private String excelName;
// sheet列表数据
private List<ExcelSheet> sheetList;
}
Aop拦截请求,将 ExcelExportResponse 处理成excel导出
@Component
@Aspect
public class ExcelExportAspect {
@Around(value = "execution(* com.itsoku..*Controller.*(..))")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
Object result = proceedingJoinPoint.proceed();
if (result instanceof ExcelExportResponse) {
//下载excel
ExcelExportUtils.writeExcelToResponse((ExcelExportResponse) result);
return null;
} else {
return result;
}
}
}
核心代码
源码如下:
public class ExcelExportField {
/**
* 字段的名称
*/
private String fieldName;
/**
* 字段描述
*/
private String fieldDesc;
public ExcelExportField() {
}
public ExcelExportField(String fieldName, String fieldDesc) {
this.fieldName = fieldName;
this.fieldDesc = fieldDesc;
}
public String getFieldName() {
return fieldName;
}
public void setFieldName(String fieldName) {
this.fieldName = fieldName;
}
public String getFieldDesc() {
return fieldDesc;
}
public void setFieldDesc(String fieldDesc) {
this.fieldDesc = fieldDesc;
}
}
import java.util.List;
public class ExcelExportRequest {
/**
* excel名称
*/
private String excelName;
/**
* sheet的名称
*/
private String sheetName;
/**
* 导出字段有序列表
*/
private List<ExcelExportField> fieldList;
public String getSheetName() {
return sheetName;
}
public void setSheetName(String sheetName) {
this.sheetName = sheetName;
}
public String getExcelName() {
return excelName;
}
public void setExcelName(String excelName) {
this.excelName = excelName;
}
public List<ExcelExportField> getFieldList() {
return fieldList;
}
public void setFieldList(List<ExcelExportField> fieldList) {
this.fieldList = fieldList;
}
}
import java.util.List;
public class ExcelExportResponse {
//导出的excel文件名称
private String excelName;
// sheet列表数据
private List<ExcelSheet> sheetList;
public String getExcelName() {
return excelName;
}
public void setExcelName(String excelName) {
this.excelName = excelName;
}
public List<ExcelSheet> getSheetList() {
return sheetList;
}
public void setSheetList(List<ExcelSheet> sheetList) {
this.sheetList = sheetList;
}
}
import cn.hutool.core.net.URLEncodeUtil;
import cn.hutool.core.util.ReflectUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.itsoku.lesson007.utils.CollUtils;
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.util.*;
import java.util.stream.Collectors;
public class ExcelExportUtils {
public static ExcelExportResponse build(List<?> dataList, ExcelExportRequest request) {
//1、组装excel导出的结果
ExcelExportResponse result = new ExcelExportResponse();
result.setExcelName(request.getExcelName());
//2、组装sheet
List<ExcelSheet> sheetList = new ArrayList<>();
result.setSheetList(sheetList);
//第1个sheet
ExcelSheet excelSheet = new ExcelSheet();
//设置sheet的名称
excelSheet.setSheetName(request.getSheetName());
//设置sheet的头
excelSheet.setHeadList(buildSheetHeadList(request));
//设置sheet中表格的数据,是个二位数组,类型 List<Map<String, Object>>
excelSheet.setDataList(buildSheetDataList(dataList, request));
//将第1个sheet放入sheet列表
sheetList.add(excelSheet);
return result;
}
public static List<ExcelHead> buildSheetHeadList(ExcelExportRequest request) {
//排序
List<ExcelExportField> fieldList = request.getFieldList();
List<ExcelHead> excelHeadList = new ArrayList<>(fieldList.size());
for (ExcelExportField excelExportField : fieldList) {
ExcelHead excelHead = new ExcelHead();
excelHead.setFieldName(excelExportField.getFieldName());
excelHead.setFieldDesc(excelExportField.getFieldDesc());
excelHeadList.add(excelHead);
}
return excelHeadList;
}
public static List<Map<String, String>> buildSheetDataList(List<?> dataList, ExcelExportRequest request) {
if (CollUtils.isEmpty(dataList)) {
return CollUtils.emptyArrayList();
}
List<Map<String, String>> sheetDataList = new ArrayList<>(dataList.size());
List<ExcelExportField> fieldList = request.getFieldList();
List<String> exportFieldNameList = CollUtils.convertList(fieldList, ExcelExportField::getFieldName);
for (Object data : dataList) {
Map<String, String> dataMap = new HashMap<>();
for (String fileName : exportFieldNameList) {
Object filedValue = ReflectUtil.getFieldValue(data, fileName);
dataMap.put(fileName, convertToString(filedValue));
}
sheetDataList.add(dataMap);
}
return sheetDataList;
}
private static String convertToString(Object obj) {
return obj == null ? "" : obj.toString();
}
public static void writeExcelToResponse(ExcelExportResponse excelExportResult) throws IOException {
HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
OutputStream outputStream = response.getOutputStream();
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncodeUtil.encode(excelExportResult.getExcelName() + ".xlsx"));
write(excelExportResult, outputStream);
}
/**
* 将excel写入到指定的流中
*
* @param result
* @param outputStream
*/
public static void write(ExcelExportResponse result, OutputStream outputStream) {
List<ExcelSheet> sheetList = result.getSheetList();
try (ExcelWriter writer = EasyExcel.write(outputStream).build();) {
for (int sheetNo = 0; sheetNo < sheetList.size(); sheetNo++) {
ExcelSheet excelSheet = sheetList.get(sheetNo);
List<List<String>> head = ExcelExportUtils.buildEasyExcelHead(excelSheet);
List<List<String>> dataList = ExcelExportUtils.buildEasyExcelDataList(excelSheet);
WriteSheet writeSheet = EasyExcel
.writerSheet(sheetNo, excelSheet.getSheetName())
.head(head).build();
writer.write(dataList, writeSheet);
}
}
}
/**
* 通过 ExcelSheet 得到easyExcel中当前sheet需要的头
*
* @param excelSheet
* @return
*/
public static List<List<String>> buildEasyExcelHead(ExcelSheet excelSheet) {
if (excelSheet == null || excelSheet.getHeadList() == null) {
return CollUtils.newArrayList();
}
return excelSheet.getHeadList().stream().map(item -> CollUtils.newArrayList(item.getFieldDesc())).collect(Collectors.toList());
}
/**
* 通过 ExcelSheet 得到easyExcel中当前sheet需要的数据
*
* @param excelSheet
* @return
*/
public static List<List<String>> buildEasyExcelDataList(ExcelSheet excelSheet) {
if (excelSheet == null || excelSheet.getHeadList() == null || excelSheet.getDataList() == null) {
return CollUtils.newArrayList();
}
List<String> filedNameList = CollUtils.convertList(excelSheet.getHeadList(), ExcelHead::getFieldName);
List<List<String>> dataList = new ArrayList<>(excelSheet.getDataList().size());
for (Map<String, String> row : excelSheet.getDataList()) {
List<String> list = new ArrayList<>();
for (String filedName : filedNameList) {
list.add(row.get(filedName));
}
dataList.add(list);
}
return dataList;
}
}
public class ExcelHead {
// 头的filedName
private String fieldName;
// 头的名称
private String fieldDesc;
public String getFieldName() {
return fieldName;
}
public void setFieldName(String fieldName) {
this.fieldName = fieldName;
}
public String getFieldDesc() {
return fieldDesc;
}
public void setFieldDesc(String fieldDesc) {
this.fieldDesc = fieldDesc;
}
}
import java.util.List;
import java.util.Map;
public class ExcelSheet {
//sheet名称
private String sheetName;
//sheet的头
private List<ExcelHead> headList;
/**
* sheet中的数据是一个表格,这里我们使用List<Map<String, String>>类型表示的
* 每行数据库,我们是放在Map<String,String>中,key就是头的filedName,value就是这个字段的值
*/
private List<Map<String, String>> dataList;
public String getSheetName() {
return sheetName;
}
public void setSheetName(String sheetName) {
this.sheetName = sheetName;
}
public List<Map<String, String>> getDataList() {
return dataList;
}
public void setDataList(List<Map<String, String>> dataList) {
this.dataList = dataList;
}
public List<ExcelHead> getHeadList() {
return headList;
}
public void setHeadList(List<ExcelHead> headList) {
this.headList = headList;
}
}
import com.excel.ExcelExportResponse;
import com.excel.ExcelExportUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class ExcelExportAspect {
@Around(value = "execution(* com..*Controller.*(..))")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
Object result = proceedingJoinPoint.proceed();
if (result instanceof ExcelExportResponse) {
//下载excel
ExcelExportUtils.writeExcelToResponse((ExcelExportResponse) result);
return null;
} else {
return result;
}
}
}
import cn.hutool.json.JSON;
import cn.hutool.json.JSONUtil;
import com.itsoku.lesson007.dto.UserExportRequest;
import com.itsoku.lesson007.excel.ExcelExportResponse;
import com.itsoku.lesson007.excel.ExcelExportUtils;
import com.itsoku.lesson007.service.UserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
@Controller
@CrossOrigin(origins = "*")
public class UserController {
private static final Logger LOGGER = LoggerFactory.getLogger(UserController.class);
@Autowired
private UserService userService;
@GetMapping("/userList")
public String userList(Model model) {
model.addAttribute("userList", this.userService.getUserList());
return "userList";
}
@PostMapping("/userExport")
@ResponseBody
public ExcelExportResponse userExport(@RequestBody UserExportRequest userExportRequest) throws IOException {
LOGGER.info("userExportRequest:{}", JSONUtil.toJsonPrettyStr(userExportRequest));
return this.userService.userExport(userExportRequest);
}
}
public class User {
private Integer userId;
private String userName;
private Integer age;
private String address;
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
import com.itsoku.lesson007.excel.ExcelExportRequest;
import java.util.List;
public class UserExportRequest extends ExcelExportRequest {
/**
* 要导出的用户id列表,不传,则导出所有用户记录
*/
private List<Integer> userIdList;
public List<Integer> getUserIdList() {
return userIdList;
}
public void setUserIdList(List<Integer> userIdList) {
this.userIdList = userIdList;
}
}
import cn.hutool.core.collection.CollectionUtil;
import com.dto.User;
import com.dto.UserExportRequest;
import com.excel.ExcelExportResponse;
import com.excel.ExcelExportUtils;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* <b>description</b>: Java高并发、微服务、性能优化实战案例100讲,视频号:程序员路人,源码 & 文档 & 技术支持,请加个人微信号:itsoku <br>
* <b>time</b>:2024/4/1 14:10 <br>
* <b>author</b>:ready likun_557@163.com
*/
@Service
public class UserService {
public List<User> list = new ArrayList<>();
public UserService() {
for (int i = 1; i <= 10; i++) {
User user = new User();
user.setUserId(i);
user.setUserName("用户名-" + i);
user.setAge(20 + i);
user.setAddress("地址-" + i);
list.add(user);
}
}
public List<User> getUserList() {
return list;
}
/**
* 根据用户id列表查找用户列表
*
* @param userIdList
* @return
*/
public List<User> getUserList(List<Integer> userIdList) {
return this.getUserList().stream().filter(item -> userIdList.contains(item.getUserId())).collect(Collectors.toList());
}
/**
* 导出用户数据
*
* @param request
* @return
*/
public ExcelExportResponse userExport(UserExportRequest request) {
List<Integer> userIdList = request.getUserIdList();
//根据用户id列表获取用户列表
List<User> userList;
if (CollectionUtil.isEmpty(userIdList)) {
userList = this.getUserList();
} else {
userList = this.getUserList(request.getUserIdList());
}
return ExcelExportUtils.build(userList, request);
}
}
import org.apache.commons.collections4.CollectionUtils;
import java.util.*;
import java.util.function.*;
import java.util.stream.Collectors;
public class CollUtils {
private CollUtils() {
throw new IllegalStateException("Utility class");
}
public static <T> ArrayList<T> emptyArrayList() {
return new ArrayList<>();
}
public static <K, V> HashMap<K, V> emptyHashMap() {
return new HashMap<>();
}
public static <K, V> LinkedHashMap<K, V> emptyLinkedHashMap() {
return new LinkedHashMap<>();
}
public static <T> HashSet<T> emptyHashSet() {
return new HashSet<>();
}
/**
* 判断集合是否是为空
*
* @param map
* @return
*/
public static boolean isEmpty(Map map) {
return (map == null || map.isEmpty());
}
/**
* 判断集合是否不为空
*
* @param map
* @return
*/
public static boolean isNotEmpty(Map map) {
return !isEmpty(map);
}
/**
* 判断集合是否是为空
*
* @param coll
* @return
*/
public static boolean isEmpty(Collection<?> coll) {
return (coll == null || coll.isEmpty());
}
/**
* 判断集合是否不为空
*
* @param coll
* @return
*/
public static boolean isNotEmpty(Collection<?> coll) {
return !isEmpty(coll);
}
/**
* 判断集合是否不为空
*
* @param coll
* @return
*/
public static <T> T isNotEmpty(Collection<?> coll, Supplier<T> supplier) {
if (isNotEmpty(coll)) {
return supplier.get();
}
return null;
}
/**
* 去重
*
* @param list
* @param <T>
* @return
*/
public static <T> Collection<T> distinct(Collection<T> list) {
return isEmpty(list) ? list : distinct(list, item -> item);
}
/**
* 去重
*
* @param from
* @param func
* @param <T>
* @param <R>
* @return
*/
public static <T, R> List<R> distinct(Collection<T> from, Function<T, R> func) {
return distinct(from, func, t -> true);
}
/**
* 去重
*
* @param from
* @param func
* @param filter
* @param <T>
* @param <R>
* @return
*/
public static <T, R> List<R> distinct(Collection<T> from, Function<T, R> func, Predicate<T> filter) {
if (isEmpty(from)) {
return new ArrayList<>();
}
return from.stream().filter(filter).map(func).distinct().collect(Collectors.toList());
}
/**
* 将一个集合转换为另外一个集合
*
* @param from
* @param func
* @param <T>
* @param <U>
* @return
*/
public static <T, U> List<U> convertList(Collection<T> from, Function<T, U> func) {
if (isEmpty(from)) {
return new ArrayList<>();
}
return from.stream().map(func).collect(Collectors.toList());
}
/**
* 将一个集合转换为另外一个集合:from->filter->list
*
* @param from
* @param func
* @param filter
* @param <T>
* @param <U>
* @return
*/
public static <T, U> List<U> convertList(Collection<T> from, Function<T, U> func, Predicate<T> filter) {
if (isEmpty(from)) {
return new ArrayList<>();
}
return from.stream().filter(filter).map(func).collect(Collectors.toList());
}
/**
* 将集合转换为set
*
* @param from
* @param func
* @param <T>
* @param <U>
* @return
*/
public static <T, U> Set<U> convertSet(Collection<T> from, Function<T, U> func) {
if (isEmpty(from)) {
return new HashSet<>();
}
return from.stream().map(func).collect(Collectors.toSet());
}
/**
* 将集合转换为set:from->filter->list
*
* @param from
* @param func
* @param filter
* @param <T>
* @param <U>
* @return
*/
public static <T, U> Set<U> convertSet(Collection<T> from, Function<T, U> func, Predicate<T> filter) {
if (isEmpty(from)) {
return new HashSet<>();
}
return from.stream().filter(filter).map(func).collect(Collectors.toSet());
}
/**
* 将集合转换为map
*
* @param from
* @param keyFunc
* @param <T>
* @param <K>
* @return
*/
public static <T, K> Map<K, T> convertMap(Collection<T> from, Function<T, K> keyFunc) {
if (isEmpty(from)) {
return new HashMap<>();
}
return convertMap(from, keyFunc, Function.identity());
}
/**
* 将集合转换为map
*
* @param from
* @param keyFunc
* @param supplier
* @param <T>
* @param <K>
* @return
*/
public static <T, K> Map<K, T> convertMap(Collection<T> from, Function<T, K> keyFunc, Supplier<? extends Map<K, T>> supplier) {
if (isEmpty(from)) {
return supplier.get();
}
return convertMap(from, keyFunc, Function.identity(), supplier);
}
/**
* 将集合转换为map
*
* @param from
* @param keyFunc
* @param valueFunc
* @param <T>
* @param <K>
* @param <V>
* @return
*/
public static <T, K, V> Map<K, V> convertMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc) {
if (isEmpty(from)) {
return new HashMap<>();
}
return convertMap(from, keyFunc, valueFunc, (v1, v2) -> v1);
}
/**
* 将集合转换为map
*
* @param from
* @param keyFunc key转换器
* @param valueFunc value转换器
* @param mergeFunction key重复时value处理策略
* @param <T>
* @param <K>
* @param <V>
* @return
*/
public static <T, K, V> Map<K, V> convertMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc, BinaryOperator<V> mergeFunction) {
if (isEmpty(from)) {
return new HashMap<>();
}
return convertMap(from, keyFunc, valueFunc, mergeFunction, HashMap::new);
}
/**
* 将集合转换为map
*
* @param from
* @param keyFunc
* @param valueFunc
* @param supplier
* @param <T>
* @param <K>
* @param <V>
* @return
*/
public static <T, K, V> Map<K, V> convertMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc, Supplier<? extends Map<K, V>> supplier) {
if (isEmpty(from)) {
return supplier.get();
}
return convertMap(from, keyFunc, valueFunc, (v1, v2) -> v1, supplier);
}
/**
* 将集合转换为map
*
* @param from
* @param keyFunc
* @param valueFunc
* @param mergeFunction
* @param supplier
* @param <T>
* @param <K>
* @param <V>
* @return
*/
public static <T, K, V> Map<K, V> convertMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc, BinaryOperator<V> mergeFunction, Supplier<? extends Map<K, V>> supplier) {
if (isEmpty(from)) {
return new HashMap<>();
}
return from.stream().collect(Collectors.toMap(keyFunc, valueFunc, mergeFunction, supplier));
}
/**
* 将集合转换为map
*
* @param from
* @param keyFunc
* @param <T>
* @param <K>
* @return
*/
public static <T, K> Map<K, List<T>> convertMultiMap(Collection<T> from, Function<T, K> keyFunc) {
if (isEmpty(from)) {
return new HashMap<>();
}
return from.stream().collect(Collectors.groupingBy(keyFunc, Collectors.mapping(t -> t, Collectors.toList())));
}
/**
* 将集合转换为map
*
* @param from
* @param keyFunc
* @param <T>
* @param <K>
* @return
*/
public static <T, K, V> Map<K, List<V>> convertMultiMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc) {
if (isEmpty(from)) {
return new HashMap<>();
}
return from.stream()
.collect(Collectors.groupingBy(keyFunc, Collectors.mapping(valueFunc, Collectors.toList())));
}
/**
* 创建 ArrayList
*
* @param args
* @return
*/
public static <E> List<E> newArrayList(E... args) {
return new ArrayList(Arrays.asList(args));
}
/**
* 创建 ArrayList
*
* @param initialCapacity
* @return
*/
public static <E> List<E> newArrayListCapacity(int initialCapacity) {
return new ArrayList(initialCapacity);
}
/**
* 创建HashSet
*
* @param args
* @return
*/
public static <E> Set<E> newHashSet(E... args) {
return new HashSet<>(Arrays.asList(args));
}
/**
* 创建LinkedHashSet
*
* @param args
* @return
*/
public static <E> Set<E> newLinkedHashSet(E... args) {
return new LinkedHashSet<>(Arrays.asList(args));
}
/**
* 创建hashMap
*
* @param args
* @return
*/
@SuppressWarnings("rawtypes")
public static <K, V> Map<K, V> newHashMap(Object... args) {
HashMap paramMap = new HashMap();
if (args != null) {
if (args.length % 2 == 0) {
throw new RuntimeException("The length must be a multiple of 2");
}
int size = args.length / 2;
for (int i = 0; i < size; i++) {
paramMap.put(args[2 * i], args[2 * i + 1]);
}
}
return paramMap;
}
/**
* 创建LinkedHashMap
*
* @param args
* @return
*/
public static <K, V> LinkedHashMap<K, V> newLinkedHashMap(Object... args) {
LinkedHashMap paramMap = new LinkedHashMap();
if (args != null) {
if (args.length % 2 == 0) {
throw new RuntimeException("The length must be a multiple of 2");
}
int size = args.length / 2;
for (int i = 0; i < size; i++) {
paramMap.put(args[2 * i], args[2 * i + 1]);
}
}
return paramMap;
}
/**
* 都不为空返回true
*
* @param values
* @return
*/
public static boolean allNotEmpty(final Collection<?>... values) {
if (values == null) {
return false;
}
for (Collection val : values) {
if (isEmpty(val)) {
return false;
}
}
return true;
}
/**
* 都为空,返回true
*
* @param values
* @return
*/
public static boolean allEmpty(final Collection<?>... values) {
return !anyNotEmpty(values);
}
/**
* 任意一个不为空,则返回true
*
* @param values
* @return
*/
public static boolean anyNotEmpty(final Collection<?>... values) {
return firstNotEmpty(values) != null;
}
/**
* 任意一个为空,则返回true
*
* @param values
* @return
*/
public static boolean anyEmpty(final Collection<?>... values) {
return !allNotEmpty(values);
}
/**
* 返回第一个不为空的集合
*
* @param values
* @return
*/
public static Collection<?> firstNotEmpty(final Collection<?>... values) {
if (values != null) {
for (final Collection val : values) {
if (isNotEmpty(val)) {
return val;
}
}
}
return null;
}
/**
* 返回第一个元素
*
* @param list
* @param <T>
* @return
*/
public static <T> T getFirst(Collection<T> list) {
return isNotEmpty(list) ? list.iterator().next() : null;
}
/**
* 返回第一个元素的某个属性
*
* @param list
* @param <T>
* @return
*/
public static <T, R> R getFirst(Collection<T> list, Function<T, R> fun) {
T first = getFirst(list);
return first == null ? null : fun.apply(first);
}
/**
* 返回第一个元素的某个属性
*
* @param list
* @param <T>
* @return
*/
public static <T> void first(Collection<T> list, Consumer<T> consumer) {
T first = getFirst(list);
if (null != first) {
consumer.accept(first);
}
}
/**
* 根据 key 获取值
*
* @param map
* @param key
* @param <K>
* @param <V>
* @return
*/
public static <K, V> V get(Map<K, V> map, K key) {
return map != null ? map.get(key) : null;
}
/**
* 转换为 Set
*
* @param list
* @param <T>
* @return
*/
public static <T> Set<T> convertToSet(Collection<T> list) {
if (list == null) {
return null;
}
if (Set.class.isInstance(list)) {
return (Set<T>) list;
}
return list.stream().collect(Collectors.toSet());
}
/**
* 转换为 List
*
* @param list
* @param <T>
* @return
*/
public static <T> List<T> convertToList(Collection<T> list) {
if (list == null) {
return null;
}
if (List.class.isInstance(list)) {
return (List<T>) list;
}
return list.stream().collect(Collectors.toList());
}
/**
* list 中是否包含 item
*
* @param list
* @param item
* @param <T>
* @return
*/
public static <T> boolean contain(Collection<T> list, T item) {
return CollUtils.isNotEmpty(list) && list.contains(item);
}
/**
* 获取集合对象大小
*
* @param object
* @return
*/
public static int size(final Object object) {
return CollectionUtils.size(object);
}
/**
* 判断两个{@link Collection} 是否元素和顺序相同,返回{@code true}的条件是:
* <ul>
* <li>两个{@link Collection}必须长度相同</li>
* <li>两个{@link Collection}元素相同index的对象必须equals,满足{@link Objects#equals(Object, Object)}</li>
* </ul>
* 此方法来自Apache-Commons-Collections4。
*
* @param list1 列表1
* @param list2 列表2
* @return 是否相同
*/
public static boolean isEqualOrderList(final Collection<?> list1, final Collection<?> list2) {
if (list1 == null || list2 == null || list1.size() != list2.size()) {
return false;
}
final Iterator<?> it1 = list1.iterator();
final Iterator<?> it2 = list2.iterator();
Object obj1;
Object obj2;
while (it1.hasNext() && it2.hasNext()) {
obj1 = it1.next();
obj2 = it2.next();
if (false == Objects.equals(obj1, obj2)) {
return false;
}
}
// 当两个Iterable长度不一致时返回false
return false == (it1.hasNext() || it2.hasNext());
}
/**
* 判断两个集合的元素是否一样
*
* @param a
* @param b
* @param orderEqual 元素顺序是否也要一样?
* @return
*/
public static boolean isEqualCollection(final Collection<?> a, final Collection<?> b, boolean orderEqual) {
if (orderEqual) {
return isEqualOrderList(a, b);
}
return CollectionUtils.isEqualCollection(a, b);
}
}
@Data
public class Result<T> {
/**
* 编码,1:成功,其他值失败
*/
private String code;
/**
* 结果
*/
public T data;
/**
* 提示消息
*/
private String msg;
}
public class ResultUtils {
public static final String SUCCESS = "1";
public static final String ERROR = "0";
public static <T> Result<T> ok() {
return result(SUCCESS, null, null);
}
public static <T> Result<T> ok(T data) {
return result(SUCCESS, data, null);
}
public static <T> Result<T> error(String msg) {
return result(ERROR, null, msg);
}
public static <T> Result<T> result(String code, T data, String msg) {
Result<T> r = new Result<>();
r.setCode(code);
r.setData(data);
r.setMsg(msg);
return r;
}
}
@SpringBootApplication
public class Lesson008Application {
public static void main(String[] args) {
SpringApplication.run(Lesson008Application.class, args);
}
}
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>用户列表</title>
<style>
* {
font-size: 12px;
padding: 0px;
margin: 0px;
}
.export {
display: flex;
justify-content: center;
}
.title{
font-weight: bold;
font-size: 20px;
color: red
}
th,td {
padding: 5px 30px;
}
</style>
<script type="text/javascript">
function download(data, url) {
let xhr = new XMLHttpRequest();
xhr.open("POST", url);
xhr.responseType = 'blob';
xhr.setRequestHeader('Content-Type','application/json;charset=utf-8');
xhr.onload = function (){
if(this.status==200){
let blob = this.response;
if(blob && blob.size > 0){
let fileName = getFileNameFromResponse(xhr.getResponseHeader("content-disposition"));
// 创建一个临时链接并模拟点击进行下载
let a = document.createElement('a');
a.href = window.URL.createObjectURL(blob);
a.download = fileName;
a.click();
} else {
console.error("下载失败");
}
}
}
xhr.send(JSON.stringify(data));
}
// 根据响应头获取文件名
function getFileNameFromResponse(contentDisposition) {
let matchResult = /attachment;filename=(.*)/.exec(contentDisposition);
if (matchResult != null && matchResult[1]) {
return decodeURIComponent(matchResult[1].replace(/['"]/g, ""));
}
return "download";
}
function exportExcel1(event) {
event.preventDefault();
let exportRequest = {
excelName: "导出用户信息" + Date.now(),
sheetName: "导出用户信息",
fieldList: [],
};
let exportColList = document.querySelector("#form1").querySelectorAll(".exportCol");
exportColList.forEach(item => {
if (item.checked) {
let exportField = {
fieldName: item.dataset.fieldName,
fieldDesc: item.dataset.fieldDesc
};
exportRequest.fieldList.push(exportField)
}
});
download(exportRequest, "http://localhost:8080/userExport");
requestExportExcel(exportRequest);
}
function exportExcel2(event) {
event.preventDefault();
let exportRequest = {
excelName: "导出用户信息" + Date.now(),
sheetName: "导出用户信息",
fieldList: [],
userIdList: []
};
let userIdEleList = document.querySelectorAll(".userId");
userIdEleList.forEach(item=>{
if (item.checked) {
exportRequest.userIdList.push(item.dataset.userId);
}
});
let exportColList = document.querySelector("#form2").querySelectorAll(".exportCol");
exportColList.forEach(item => {
if (item.checked) {
let exportField = {
fieldName: item.dataset.fieldName,
fieldDesc: item.dataset.fieldDesc
};
exportRequest.fieldList.push(exportField)
}
});
download(exportRequest, "http://localhost:8080/userExport");
}
</script>
</head>
<body>
<h1 style="color: white; font-weight: bold; text-align: center; background: red; font-size: 25px; line-height: 40px">通用的Excel导出工具实战,导出的列可以动态指定(如需要导出哪些列、列的顺序都可以自定义、列的名称)</h1>
<br><br>
<div class="export">
<form id="form1">
<table border="1" cellspacing="0" cellpadding="0">
<caption>
<span class="title">案例1:请先勾选需要导出的列,然后点击</span>
<button class="exportBtn" onclick="exportExcel1(event)">导出</button>
<br/>
<br/>
</caption>
<tr>
<th><label><input type="checkbox" class="exportCol" data-field-name="userId" data-field-desc="用户id"> 用户id</label></th>
<th><label><input type="checkbox" class="exportCol" data-field-name="userName" data-field-desc="用户名">用户名</label></th>
<th><label><input type="checkbox" class="exportCol" data-field-name="age" data-field-desc="年龄">年龄</label></th>
<th><label><input type="checkbox" class="exportCol" data-field-name="address"data-field-desc="地址">地址</label></th>
</tr>
<tr th:each="user,userStat:${userList}">
<td th:text="${user.userId}"></td>
<td th:text="${user.userName}"></td>
<td th:text="${user.age}"></td>
<td th:text="${user.address}"></td>
</tr>
</table>
</form>
</div>
<br>
<hr/>
<br><br>
<div class="export">
<form id="form2">
<table border="1" cellspacing="0" cellpadding="0">
<caption>
<span class="title">案例2:请先勾选需要导出的列 & 需要导出的行,然后点击</span>
<button class="exportBtn" onclick="exportExcel2(event)">导出</button>
<br/>
<br/>
</caption>
<tr>
<th>选择要导出的记录</th>
<th><label><input type="checkbox" class="exportCol" data-field-name="userId" data-field-desc="用户id"> 用户id</label></th>
<th><label><input type="checkbox" class="exportCol" data-field-name="address"data-field-desc="地址">地址</label></th>
<th><label><input type="checkbox" class="exportCol" data-field-name="age" data-field-desc="年龄">年龄</label></th>
<th><label><input type="checkbox" class="exportCol" data-field-name="userName" data-field-desc="用户名">用户名</label></th>
</tr>
<tr th:each="user,userStat:${userList}">
<td><label><input type="checkbox" class="userId" th:data-user-id="${user.userId}"></label></td>
<td th:text="${user.userId}"></td>
<td th:text="${user.address}"></td>
<td th:text="${user.age}"></td>
<td th:text="${user.userName}"></td>
</tr>
</table>
</form>
</div>
</body>
</html>
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.13</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com</groupId>
<artifactId>test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>test</name>
<description>test</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-boot.version>2.7.13</spring-boot.version>
<commons.io.version>2.11.0</commons.io.version>
<mybatis-plus.version>3.5.3</mybatis-plus.version>
<hutool.version>5.8.2</hutool.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- io常用工具类 -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons.io.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
</dependency>
<!-- Apache Lang3 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!-- Apache collections4 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.3.4</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<image>
<builder>paketobuildpacks/builder-jammy-base:latest</builder>
</image>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
【注意】完整的核心代码已附上,如果运行有问题,请随时评论(我已贴出源码,所有代码都有,请需要之人从头看到尾),创作不易,请动动你发财的小手支持下谢谢。