使用 EasyExcel 封装通用 Excel 导出工具类
在企业应用中,经常需要把后台数据导出成 Excel 文件供用户下载。在我们的项目中,涉及“成品”、“材料”等模块的数据导出。本文将分享如何使用 EasyExcel 封装一个通用的 Excel 导出工具类 ExcelExportUtil
,支持单个 Sheet 和多 Sheet 导出。
1. 背景问题
在没有封装工具类之前,导出 Excel 的代码通常是这样的:
@PostMapping("/export/{orderNumber}")
private void exportModal2BillHead(@PathVariable String orderNumber, HttpServletResponse response) throws IOException {List<Modal2BillHeadVO> list = modal2BillHeadService.getQueryOrderInfo(orderNumber);// 转换为导出 VOList<Modal2BillExportVO> exportList = list.stream().map(v -> {Modal2BillExportVO vo = new Modal2BillExportVO();vo.setMaterialStockOutId(String.valueOf(v.getMaterialStockOutId()));vo.setDoCno(v.getDoCno());vo.setCreatedOn(String.valueOf(v.getCreatedOn()));vo.setCreatedBy(v.getCreatedBy());vo.setOrderNumber(v.getOrderNumber());vo.setItemCode(v.getItemCode());vo.setItemName(v.getItemName());vo.setItemSpecs(v.getItemSpecs());vo.setStoreUomoty(v.getStoreUomoty());vo.setCostUomoty(v.getCostUomoty());vo.setUom(v.getUom());vo.setWhName(v.getWhName());vo.setDoctype(v.getDoctype());vo.setTransferFormType(v.getTransferFormType());vo.setPurchasePrice(v.getPurchasePrice());return vo;}).collect(Collectors.toList());// Excel 导出逻辑String fileName = "材料提库导出";response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");response.setCharacterEncoding("utf-8");String encodedFileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + encodedFileName + ".xlsx");ServletOutputStream out = response.getOutputStream();EasyExcel.write(out, Modal2BillExportVO.class).sheet("成品提库").doWrite(exportList == null ? new ArrayList<>() : exportList);out.flush();out.close();
}
❌ 缺点:
每个导出都要重复写响应头、流操作。
代码臃肿,可维护性差。
不方便支持多 Sheet 或通用导出。
2. 封装工具类:ExcelExportUtil
为了提高可复用性,我们封装了一个通用工具类,核心设计思想:
单个 Sheet 导出:快速导出单个 Excel。
多 Sheet 导出:在一个 Excel 文件中包含多个 Sheet 标签页。
泛型支持:通过
<T>
泛型,可以导出任意带@ExcelProperty
注解的实体类。
public class ExcelExportUtil {// 单个 Sheet 导出public static <T> void export(HttpServletResponse response, String fileName, Class<T> clazz, List<T> dataList) {try {setResponseHeader(response, fileName);ServletOutputStream out = response.getOutputStream();EasyExcel.write(out, clazz).sheet(fileName).doWrite(dataList == null ? new ArrayList<>() : dataList);out.flush();out.close();} catch (Exception e) {throw new RuntimeException("Excel 导出失败:" + e.getMessage(), e);}}// 多 Sheet 导出public static void exportMultiSheet(HttpServletResponse response, String fileName, Map<String, SheetData<?>> sheets) {try {setResponseHeader(response, fileName);try (ServletOutputStream out = response.getOutputStream();ExcelWriter writer = EasyExcel.write(out).build()) {int sheetNo = 0;for (Map.Entry<String, SheetData<?>> entry : sheets.entrySet()) {String sheetName = entry.getKey();SheetData<?> sheetData = entry.getValue();WriteSheet writeSheet = EasyExcel.writerSheet(sheetNo, sheetName).head(sheetData.getClazz()).build();writer.write(sheetData.getData() == null ? new ArrayList<>() : sheetData.getData(), writeSheet);sheetNo++;}}} catch (Exception e) {throw new RuntimeException("Excel 多 Sheet 导出失败:" + e.getMessage(), e);}}// 设置响应头private static void setResponseHeader(HttpServletResponse response, String fileName) throws Exception {response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");response.setCharacterEncoding("utf-8");String encodedFileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + encodedFileName + ".xlsx");}// 多 Sheet 封装类public static class SheetData<T> {private Class<T> clazz;private List<T> data;public SheetData(Class<T> clazz, List<T> data) {this.clazz = clazz;this.data = data;}public Class<T> getClazz() { return clazz; }public List<T> getData() { return data; }}
}
3. 控制器使用示例(我只是为了方便代码量少直接写到contact层了)
✅ 优点:
代码简洁:控制器只负责数据处理,不关心 Excel 流操作。
复用性高:任何 VO/DTO 都可以直接导出。
支持多 Sheet:将来需要在同一个 Excel 文件中导出多张表时,只需调用
exportMultiSheet
即可。
4. 总结
通过 ExcelExportUtil
工具类,我们可以:
轻松实现单个 Sheet 导出。
支持多 Sheet 导出,实现复杂报表的汇总。
封装响应头和流操作,减少重复代码,提高可维护性。
💡 Tip:如果将来需要“每个表单生成独立 Excel”,只需多次调用 export
方法即可。