当前位置: 首页 > news >正文

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>

【注意】完整的核心代码已附上,如果运行有问题,请随时评论(我已贴出源码,所有代码都有,请需要之人从头看到尾),创作不易,请动动你发财的小手支持下谢谢。

相关文章:

  • 怎么做整蛊网站百度app官方正式版
  • 广州网站定制服务seo外包公司费用
  • 定制化网站开发公司北京seo平台
  • 建筑证书兼职网站排名前50名免费的网站
  • 网站布局结构图世界十大搜索引擎及地址
  • 好网站欣赏网站赚钱
  • 适配 AGP8.5 版本,版本依赖管理(五)
  • 新版本Xmind结合DeepSeek快速生成美丽的思维导图
  • hi3516cv610_new_defconfig内容
  • 异步函数 async/await的认识与学习
  • Windows cursor集成powershell(conda)
  • AI产品的上层建筑:提示词工程、RAG与Agent
  • 嵌入式硬件篇---USBUART串口
  • OpenCV 从入门到精通(day_05)
  • SSD目标检测
  • 大学生机器人比赛实战(二)软件篇
  • 探秘Transformer系列之(22)--- LoRA
  • 基于Cocos creator 实现坦克大战小游戏
  • 为什么LoRA在目标检测方向不奏效?
  • 颠覆传统医疗!基于DeepSeek的智能化导尿管技术解析与实
  • Java 8 到 Java 21 系列之 Optional 类型:优雅地处理空值(Java 8)
  • 【数据库】达梦arm64安装
  • Linux Command nmap 网络扫描
  • Handy Multi Agent 学习笔记 -Task02
  • MySQL InnoDB 教程:深入理解文件结构与优化手段
  • SpringBoot+vue前后端分离整合sa-token(无cookie登录态 详细的登录流程)