SpringBoot中使用POI+EasyExcel批量导出主子表信息,以箱单为例

如果填充模板采用写死的方式,比如第2行第3列填充什么内容,直接用POI就可以实现,不需要EasyExcel,也比较简单。

如果只导出一个单层机的信息也比较简单。

今天讲的主要是要求用替换占位符的方式填充Excel,并且数据是主子表信息,主表单条子表多条的这种数据。

  1. 首先配置模板,如下:

    其中{contract_number}{box_number}为表头信息,而下面的{list.goods_code}等则为表体信息。

  2. 将该模板文件命名为BoxList.xlsx,并放到resources/excel-template下。

  3. 引入EasyExcel依赖:

xml 复制代码
<dependency>
	<groupId>com.alibaba</groupId>
	<artifactId>easyexcel</artifactId>
	<version>4.0.3</version>
</dependency>
  1. 生成文件代码如下:
java 复制代码
Map<String, Object> boxMap = new HashMap<>();
boxMap1.put("contract_number", "HT-2026-001");
boxMap1.put("box_wrap_number", "BOX-20260423-001");

List<Map<String, Object>> detail = new ArrayList<>();
Map<String, Object> d1_1 = new HashMap<>();
d1_1.put("goods_code", "GC260424-1524");
d1_1.put......;

detail.add(d1_1);
boxMap.put("list", detail);

// 读取Excel模板
try (InputStream template = new ClassPathResource("excel-template/BoxList.xlsx").getInputStream()) {
                {
                    // excelFilePath为保存文件的路径,需要进行预创建
                    String fileName = excelFilePath + File.separator + "包装箱单_" + boxMap.get("id") + ".xlsx";
                    // 准备写入文件,声明即将写入的文件和模板
                    ExcelWriter excelWriter = EasyExcel.write(fileName)
                            .withTemplate(template)
                            .build();
                    WriteSheet writeSheet = EasyExcel.writerSheet().build();
                    // 写入表头信息
                    excelWriter.fill(boxMap, writeSheet);
                    List<Map<String, Object>> detailList = (List<Map<String, Object>>) boxMap.get("list");
                    // 写入表体的配置
                    FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
                    FillWrapper wrapper = new FillWrapper("list", detailList);
                    // 写入表体信息
                    excelWriter.fill(wrapper, fillConfig, writeSheet);
                    // 正式写入文件
                    excelWriter.finish();
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }

这样就可以成功写入文件了。注意在EasyExcel.write()......中,不要加.autoTrim()以及其他多余的东西,否则可能会写入失败。

如果需要同时生成多个箱单,并通过zip压缩方式导出,往下看。

  1. 生成多个箱单Excel,并通过zip压缩方式导出,直接上接口代码:
java 复制代码
@GetMapping(value = "/exportBoxList")
public void exportBoxList(@RequestParam(name = "ids") String ids, HttpServletResponse response) {
	List<Map<String, Object>> boxMapList = boxService.getBoxList(ids);
	List<File> fileList = new ArrayList<>(boxMapList.size());
	for (Map<String, Object> boxMap : boxMapList) {
		try (InputStream template = new ClassPathResource("excel-template/BoxList.xlsx").getInputStream()) {
			{
                                // 这里注意,箱号如果包含特殊符号,这里就不要用箱单做名字了,否则可能导致路径混乱找不到文件
				String fileName = excelFilePath + File.separator + "箱单_" + boxMap.get("box_number") + ".xlsx";
				ExcelWriter excelWriter = EasyExcel.write(fileName)
						.withTemplate(template)
						.build();
				WriteSheet writeSheet = EasyExcel.writerSheet().build();
				excelWriter.fill(boxMap, writeSheet);
				List<Map<String, Object>> detailList = (List<Map<String, Object>>) boxMap.get("list");
				FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
				FillWrapper wrapper = new FillWrapper("list", detailList);
				excelWriter.fill(wrapper, fillConfig, writeSheet);
				excelWriter.finish();
				fileList.add(new File(fileName));
			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
	try {
		response.setContentType("application/octet-stream");
		response.setHeader("Content-disposition","attachment;filename=" + URLEncoder.encode("箱单.zip", "UTF-8"));
		OutputStream os = response.getOutputStream();
		ZipUtils.downloadZipForFiles(os, fileList);
		os.flush();
		os.close();
		for (File file : fileList) {
			file.deleteOnExit();
		}
	} catch (IOException e) {
		throw new RuntimeException(e);
	}
}

就到这里。后面附上ZipUtils的代码:

java 复制代码
package org.jeecg.common.util;

import java.io.*;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import static org.springframework.util.StreamUtils.BUFFER_SIZE;
 
/**
 * @Author: wyf
 * @Date: 2026-4-23 16:02
 * @Description: zip工具类
 */
public class ZipUtils {
    /**
     * 传入文件file
     * @param outputStream
     * @param fileList
     */
    public static void downloadZipForFiles(OutputStream outputStream, List<File> fileList){
        ZipOutputStream zipOutputStream = null;
        try {
            zipOutputStream = new ZipOutputStream(outputStream);
            for (File file : fileList) {
                ZipEntry zipEntry = new ZipEntry(file.getName());
                zipOutputStream.putNextEntry(zipEntry);
                byte[] buf = new byte[BUFFER_SIZE];
                int len;
                FileInputStream in = new FileInputStream(file);
                while ((len = in.read(buf)) != -1) {
                    zipOutputStream.write(buf, 0, len);
                    zipOutputStream.flush();
                }
            }
            zipOutputStream.flush();
            zipOutputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 关闭流
            try {
                if (zipOutputStream != null ) {
                    zipOutputStream.close();
                }
                if (outputStream != null) {
                    outputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
 
    /**
     * 传入文件的 byte[]
     * Map<String,byte[]> fileBufMap key是文件名(包含后缀),value是文件的byte[]
     * @param outputStream
     * @param fileBufMap
     */
    public static void downloadZipForByteMore(OutputStream outputStream,Map<String,byte[]> fileBufMap)  {
            ZipOutputStream zipOutputStream = null;
            try {
                zipOutputStream = new ZipOutputStream(outputStream);
                for (String fileName:fileBufMap.keySet()){
                    ZipEntry zipEntry = new ZipEntry(fileName);
                    zipOutputStream.putNextEntry(zipEntry);
                    if (Objects.nonNull(fileBufMap.get(fileName))){
                        byte[] fileBytes = fileBufMap.get(fileName);
                        zipOutputStream.write(fileBytes);
                        zipOutputStream.flush();
                    }
                }
                zipOutputStream.flush();
                zipOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                // 关闭流
                try {
                    if (zipOutputStream != null ) {
                        zipOutputStream.close();
                    }
                    if (outputStream != null) {
                        outputStream.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
    }
 
    /**
     * 返回zip包的 byte[]
     *
     * @param fileBufMap
     * @return
     */
    public static byte[] getZipForByteMore(Map<String,byte[]> fileBufMap)  {
        ByteArrayOutputStream totalZipBytes = null;
        ZipOutputStream zipOutputStream = null;
        try {
            totalZipBytes = new ByteArrayOutputStream();
            zipOutputStream = new ZipOutputStream(totalZipBytes);
            for (String fileName:fileBufMap.keySet()){
                ZipEntry zipEntry = new ZipEntry(fileName);
                zipOutputStream.putNextEntry(zipEntry);
                if (Objects.nonNull(fileBufMap.get(fileName))){
                    byte[] fileBytes = fileBufMap.get(fileName);
                    zipOutputStream.write(fileBytes);
                    zipOutputStream.flush();
                }
            }
            zipOutputStream.close();
            byte[] bytes = totalZipBytes.toByteArray();
            totalZipBytes.close();
            return bytes;
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (totalZipBytes != null) {
                    totalZipBytes.close();
                }
                if (zipOutputStream != null) {
                    zipOutputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}
相关推荐
love530love37 分钟前
LiveTalking 数字人项目 Windows 部署完全指南(EPGF 架构)
人工智能·windows·python·架构·livetalking·epgf
星辰徐哥1 小时前
Spring Boot 微服务架构设计与实现
spring boot·后端·微服务
星辰徐哥1 小时前
Spring Boot 数据导入导出与报表生成
spring boot·后端·ui
明夜之约1 小时前
Spring Boot 自动装配源码
java·spring boot·后端
Leaton Lee1 小时前
Spring Boot分层架构详解:从Controller到Service再到Mapper的完整流程
java·spring boot·后端·架构
Micro麦可乐1 小时前
Spring Boot 实战:从零设计一个短链系统(含完整代码与数据库设计)
数据库·spring boot·后端·哈希算法·雪花算法·短链系统
Jinkxs1 小时前
Resilience4j- 与 Spring Boot 快速集成:自动配置与基础注解使用
java·spring boot·后端
毕设源码_郑学姐1 小时前
计算机毕业设计springboot网络相册设计与实现 基于Spring Boot框架的在线相册管理系统开发与应用 Spring Boot驱动的网络影集设计与实践
spring boot·后端·课程设计
辣机小司1 小时前
【踩坑记录:Spring Boot 配置文件读取值不一致?警惕 YAML 的“八进制陷阱”与 SnakeYAML 版本之谜】
java·spring boot·后端·yaml·踩坑记录
一条小锦吕*1 小时前
基于Spring Boot + 数据可视化 + 协同过滤算法的推荐系统设计与实现(源码+论文+部署全讲解)
spring boot·算法·信息可视化