Java實現(xiàn)Excel導(dǎo)出的全部流程(多Sheet、復(fù)雜格式)
需求:
1、根據(jù)如下圖片,導(dǎo)出同樣格式的excel文件。
2、并且list實體集合有項目號,根據(jù)項目號區(qū)分導(dǎo)出不同sheet頁。項目號格式:G10086B1,G10087B1,去掉最后兩位B1或B2在區(qū)分,比如G10086,G10086為一個sheet頁。G10087又為一個新的sheet頁。

一、依賴引入(Maven)
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.2.5</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.5</version>
</dependency>
二、完整的實體類定義
import lombok.Data;
import java.io.Serializable;
/**
- 入庫單實體類(完全對應(yīng)Excel表格字段)
*/
@Data
public class WarehouseIn implements Serializable {
private static final long serialVersionUID = 1L;
// 1. 序號(Excel第1列,導(dǎo)出時自動生成,無需手動設(shè)置)
private Integer serialNo;
// 2. 日期(Excel第2列)
private String date;
// 3. 所屬車間(Excel第3列)
private String workshop;
// 4. 項目號(Excel第4列,格式如GPC25061095B2,用于分組Sheet)
private String projectNo;
// 5. 機臺(Excel第5列)
private String machine;
// 6. 模號(Excel第6列)
private String modelNo;
// 7. 任務(wù)包編碼(Excel第7列)
private String taskPackageCode;
// 8. 任務(wù)包名稱(Excel第8列)
private String taskPackageName;
// 9. 入庫數(shù)量(Excel第9列)
private Integer inStockQty;
// 10. 實收數(shù)(Excel第10列)
private Integer actualReceivedQty;
// 11. 班(Excel第11列,如:早班/中班/晚班)
private String workShift;
// 12. 組(Excel第12列,如:1組/2組)
private String workGroup;
// 13. 組員(Excel第13列,如:張三/李四)
private String groupMember;
// 14. 備注(Excel第14列)
private String remarks;
// 擴展字段(Excel中隱藏的輔助字段,用于填充表頭信息)
// 填單日期(Excel表頭-填單日期)
private String fillDate;
// 交付批次(Excel表頭-交付批次,如:第二批(2+2))
private String deliveryBatch;
// 單號(Excel表頭-單號,如:GEN2025131)
private String orderNo;
}
實體類說明:
- 字段對應(yīng)關(guān)系:每個字段與 Excel 表格列完全對齊,字段名采用「業(yè)務(wù)語義 + 字段類型」命名,便于理解和維護。
- 擴展字段:fillDate(填單日期)、deliveryBatch(交付批次)、orderNo(單號)是 Excel 表頭的動態(tài)信息,實體類中新增這些字段用于填充,避免硬編碼。
- 序列化:實現(xiàn)Serializable接口,支持分布式場景下的數(shù)據(jù)傳輸(如 Redis 緩存、RPC 調(diào)用)。
- Lombok 注解:使用@Data自動生成getter/setter/toString等方法,簡化代碼。
三、代碼實現(xiàn)
1、導(dǎo)出工具
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.OutputStream;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class ExcelExportUtil {
/**
* 生產(chǎn)入庫單導(dǎo)出
*/
@Override
public void exportWarehouseInExcel(OutputStream outputStream, List<StockInEntity> list) throws IOException {
// 按項目號分組(去掉最后兩位,如GPC25061095B2 → GPC25061095)
Map<String, List<StockInEntity>> groupByProject = list.stream()
.filter(item -> isValidProjectNumber(item.getProjectNumber()))
.collect(Collectors.groupingBy(item -> truncateProjectNumber(item.getProjectNumber())));
// 創(chuàng)建Workbook并自動關(guān)閉
try (Workbook workbook = new XSSFWorkbook()) {
// 為每個分組創(chuàng)建Sheet
for (Map.Entry<String, List<StockInEntity>> entry : groupByProject.entrySet()) {
String sheetName = entry.getKey() + "項目"; // Sheet名(如GPC25061095項目)
Sheet sheet = workbook.createSheet(sheetName);
List<StockInEntity> sheetData = entry.getValue();
// 構(gòu)建當前Sheet的入庫單內(nèi)容
buildStockInEntitySheet(sheet, sheetData);
}
// 寫出Excel
workbook.write(outputStream);
}
}
// 判斷項目編號是否合法
private boolean isValidProjectNumber(String projectNo) {
return projectNo != null && projectNo.length() >= 2;
}
// 截取項目編號(去掉最后兩位)
private String truncateProjectNumber(String projectNo) {
if (!isValidProjectNumber(projectNo)) {
throw new IllegalArgumentException("Invalid project number: " + projectNo);
}
return projectNo.substring(0, projectNo.length() - 2);
}
/**
* 構(gòu)建單個Sheet的入庫單格式
*/
private static void buildStockInEntitySheet(Sheet sheet, List<StockInEntity> data) {
// 指定時區(qū)
ZoneId zoneId = ZoneId.of("Asia/Shanghai");
// 獲取指定時區(qū)的當前日期
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
String formattedDate = LocalDate.now(zoneId).format(formatter);
// ========== 設(shè)置列寬 ==========
int[] columnWidths = {2500, 3500, 5000, 6000, 4000, 4500, 5500, 6500, 3000, 3000, 3000, 3000, 7000, 3000};
for (int i = 0; i < columnWidths.length; i++) {
sheet.setColumnWidth(i, columnWidths[i]);
}
// ========== 創(chuàng)建樣式 ==========
Workbook workbook = sheet.getWorkbook();
// 標題樣式(居中、加粗)
CellStyle titleStyle = workbook.createCellStyle();
Font titleFont = workbook.createFont();
titleFont.setBold(true);
titleFont.setFontHeightInPoints((short) 16);
titleStyle.setFont(titleFont);
titleStyle.setAlignment(HorizontalAlignment.CENTER);
titleStyle.setVerticalAlignment(VerticalAlignment.CENTER);
// 表頭樣式(灰色背景、居中)
CellStyle headerStyle = workbook.createCellStyle();
Font headerFont = workbook.createFont();
headerFont.setBold(true);
headerFont.setColor(IndexedColors.WHITE.getIndex());
headerStyle.setFont(headerFont);
headerStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
headerStyle.setAlignment(HorizontalAlignment.CENTER);
headerStyle.setVerticalAlignment(VerticalAlignment.CENTER);
headerStyle.setBorderTop(BorderStyle.THIN);
headerStyle.setBorderBottom(BorderStyle.THIN);
headerStyle.setBorderLeft(BorderStyle.THIN);
headerStyle.setBorderRight(BorderStyle.THIN);
// 內(nèi)容樣式(居中、邊框)
CellStyle contentStyle = workbook.createCellStyle();
Font row4Font = workbook.createFont();
row4Font.setBold(true);
contentStyle.setFont(row4Font);
contentStyle.setAlignment(HorizontalAlignment.CENTER);
contentStyle.setVerticalAlignment(VerticalAlignment.CENTER);
contentStyle.setBorderTop(BorderStyle.THIN);
contentStyle.setBorderBottom(BorderStyle.THIN);
contentStyle.setBorderLeft(BorderStyle.THIN);
contentStyle.setBorderRight(BorderStyle.THIN);
// ========== 構(gòu)建Sheet內(nèi)容 ==========
// 第1行:入庫單標題(合并單元格)
Row row1 = sheet.createRow(0);
Cell cell1 = row1.createCell(0);
cell1.setCellValue("入庫單");
cell1.setCellStyle(titleStyle);
sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, 13)); // 合并A1-N1
// 第2行:填單日期、項目號等信息(合并單元格)
CellStyle row2Style = workbook.createCellStyle();
Font row2Font = workbook.createFont();
row2Font.setUnderline(Font.U_SINGLE); // 單下劃線(也可以用U_DOUBLE表示雙下劃線)
row2Font.setBold(true);
row2Style.setFont(row2Font);
row2Style.setFillForegroundColor(IndexedColors.WHITE.getIndex());
row2Style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
row2Style.setAlignment(HorizontalAlignment.CENTER);
row2Style.setVerticalAlignment(VerticalAlignment.CENTER);
// 創(chuàng)建行并設(shè)置高度
Row row2 = sheet.createRow(1);
row2.setHeightInPoints((short) 35);
// 填單日期(A2-B2)
Cell cell2A = row2.createCell(0);
cell2A.setCellValue("填單日期:" + formattedDate);
cell2A.setCellStyle(row2Style);
sheet.addMergedRegion(new CellRangeAddress(1, 1, 0, 1));
// 項目號(C2-F2)
Cell cell2C = row2.createCell(2);
cell2C.setCellValue("項目號:" + data.get(0).getProjectNumber().substring(0, data.get(0).getProjectNumber().length() - 2));
cell2C.setCellStyle(row2Style);
sheet.addMergedRegion(new CellRangeAddress(1, 1, 2, 5));
// 交付批次(G2-J2)
Cell cell2G = row2.createCell(6);
cell2G.setCellValue("交付批次:" + data.get(0).getDeliveryBatch());
cell2G.setCellStyle(row2Style);
sheet.addMergedRegion(new CellRangeAddress(1, 1, 6, 9));
// 單號(K2-N2)
Cell cell2K = row2.createCell(10);
cell2K.setCellValue("單號:" + data.get(0).getInputNo());
cell2K.setCellStyle(row2Style);
sheet.addMergedRegion(new CellRangeAddress(1, 1, 10, 13));
// 第3行:表頭(序號、日期、所屬車間...)
Row row3 = sheet.createRow(2);
row3.setHeightInPoints((short) 50);
String[] headers = {"序號", "日期", "所屬車間", "項目號", "機臺", "模號", "任務(wù)包編碼", "任務(wù)包名稱", "入庫數(shù)量", "實收數(shù)", "班", "組", "組員", "備注"};
for (int i = 0; i < headers.length; i++) {
Cell cell = row3.createCell(i);
cell.setCellValue(headers[i]);
cell.setCellStyle(headerStyle);
}
// 第4行及以后:數(shù)據(jù)行
for (int i = 0; i < data.size(); i++) {
StockInEntity item = data.get(i);
Row row = sheet.createRow(3 + i);
row.setHeightInPoints((short) 45);
// 序號
Cell cell0 = row.createCell(0);
cell0.setCellValue(i + 1);
cell0.setCellStyle(contentStyle);
// 日期
Cell cell11 = row.createCell(1);
cell11.setCellValue(item.getCreateTime().format(formatter));
cell11.setCellStyle(contentStyle);
// 所屬車間
Cell cell2 = row.createCell(2);
cell2.setCellValue(item.getReceiveDeptName());
cell2.setCellStyle(contentStyle);
// 項目號
Cell cell3 = row.createCell(3);
cell3.setCellValue(item.getProjectNumber());
cell3.setCellStyle(contentStyle);
// 機臺
Cell cell4 = row.createCell(4);
cell4.setCellValue(item.getMachine());
cell4.setCellStyle(contentStyle);
// 模號
Cell cell5 = row.createCell(5);
cell5.setCellValue(item.getModelNo());
cell5.setCellStyle(contentStyle);
// 任務(wù)包編碼
Cell cell6 = row.createCell(6);
cell6.setCellValue(item.getMaterialCode());
cell6.setCellStyle(contentStyle);
// 任務(wù)包名稱
Cell cell7 = row.createCell(7);
cell7.setCellValue(item.getMaterialName());
cell7.setCellStyle(contentStyle);
// 入庫數(shù)量
Cell cell8 = row.createCell(8);
cell8.setCellValue(String.valueOf(item.getQty()));
cell8.setCellStyle(contentStyle);
// 實收數(shù)
Cell cell9 = row.createCell(9);
cell9.setCellValue(String.valueOf(item.getQty()));
cell9.setCellStyle(contentStyle);
// 班、組、組員、備注(示例中為空,可根據(jù)實際數(shù)據(jù)填充)
for (int j = 10; j < 14; j++) {
Cell cell = row.createCell(j);
cell.setCellStyle(contentStyle);
}
}
}
}
2、使用示例(Web 場景)
在 Controller 中調(diào)用工具類,導(dǎo)出 Excel:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.List;
@RestController
public class ExportController {
/**
* 生產(chǎn)入庫單導(dǎo)出
*/
@PostMapping("/exportProInput")
public void exportProInput(HttpServletResponse response, InputQueryDTO input) {
List<StockInEntity> list = baseService.selectProInput(input);
PaAssert.isError(CollUtil.isEmpty(list), "導(dǎo)出數(shù)據(jù)不能為空");
try {
// 設(shè)置響應(yīng)頭
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
String fileName = URLEncoder.encode("入庫單.xlsx", "UTF-8");
response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
// 導(dǎo)出Excel
OutputStream outputStream = response.getOutputStream();
baseService.exportWarehouseInExcel(outputStream, list);
outputStream.flush();
} catch (IOException e) {
e.printStackTrace();
throw new BaseException("導(dǎo)出失敗");
}
}
// 模擬數(shù)據(jù)(實際從數(shù)據(jù)庫查詢)
private List<StockInEntity> getWarehouseInData() {
// 示例數(shù)據(jù),實際替換為業(yè)務(wù)數(shù)據(jù)
StockInEntity= new StockInEntity();
item1.setProjectNo("GPC25061095B2");
item1.setDate("2025/12/16");
item1.setWorkshop("精密裝配");
item1.setMachine("J20-04");
item1.setModel("5400002400");
item1.setTaskCode("導(dǎo)向壓銷部件2");
item1.setTaskName("導(dǎo)向壓銷部件2");
item1.setInQty(2);
item1.setActualQty(2);
// 可添加更多數(shù)據(jù)...
return List.of(item1, item1); // 示例中重復(fù)添加,實際替換為真實數(shù)據(jù)
}
}
四、導(dǎo)出效果

總結(jié)
- Sheet 分組邏輯:通過projectNo.substring(0, projectNo.length() - 2)截取項目號前綴,實現(xiàn)同一前綴的記錄分到同一個 Sheet。
- 格式還原:通過單元格合并、樣式設(shè)置(背景色、邊框、對齊方式)還原示例中的Excel 格式。
- 動態(tài)數(shù)據(jù):代碼中 “填單日期、交付批次、單號” 為固定值,實際應(yīng)從業(yè)務(wù)數(shù)據(jù)中動態(tài)獲?。稍趯嶓w類中添加對應(yīng)字段)。
到此這篇關(guān)于Java實現(xiàn)Excel導(dǎo)出的全部流程(多Sheet、復(fù)雜格式)的文章就介紹到這了,更多相關(guān)Java實現(xiàn)Excel導(dǎo)出內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java 數(shù)據(jù)結(jié)構(gòu)鏈表操作實現(xiàn)代碼
這篇文章主要介紹了Java 數(shù)據(jù)結(jié)構(gòu)鏈表操作的相關(guān)資料,并附實例代碼,需要的朋友可以參考下2016-10-10
基于Java并發(fā)容器ConcurrentHashMap#put方法解析
下面小編就為大家?guī)硪黄贘ava并發(fā)容器ConcurrentHashMap#put方法解析。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-06-06
SpringBoot安全策略開發(fā)之集成數(shù)據(jù)傳輸加密
這篇文章主要介紹了SpringBoot集成數(shù)據(jù)傳輸加密,近期在對開發(fā)框架安全策略方面進行升級優(yōu)化,提供一些通用場景的解決方案,本文針對前后端數(shù)據(jù)傳輸加密進行簡單的分享2023-01-01
java工具類static靜態(tài)方法讀取yml配置過程
文章介紹了在工具類中獲取YAML配置時遇到的問題,由于變量是靜態(tài)的,而Spring加載靜態(tài)方法比IOC容器早,導(dǎo)致無法直接使用@Value注解讀取YAML配置,從而讀取結(jié)果為null2024-11-11
springboot RESTful以及參數(shù)注解的使用方式
這篇文章主要介紹了springboot RESTful以及參數(shù)注解的使用方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-10-10

