动态生成带图片的Excel报表EasyExcel 3.x实战指南每次手动在Excel里调整图片位置时我都忍不住想摔键盘——直到发现EasyExcel的模板填充功能。上周公司HR部门提出新需求要为300名新员工批量生成带照片的工牌Excel如果手动操作至少需要两天。而用下面这个方法我们只用了20分钟就完成了全部报表生成。1. 环境准备与模板设计1.1 依赖配置要点在Spring Boot项目中引入关键依赖时版本兼容性是个隐形杀手。推荐使用以下组合dependency groupIdcom.alibaba/groupId artifactIdeasyexcel/artifactId version3.1.1/version /dependency dependency groupIdorg.apache.poi/groupId artifactIdpoi/artifactId version5.2.3/version /dependency注意POI 4.x以上版本与EasyExcel 3.x存在图片写入兼容性问题实测5.2.3版本最稳定1.2 智能模板设计技巧设计模板时这些细节能让后续开发事半功倍使用${variable}作为文本占位符图片位置预留空白单元格并添加批注!-- image:fieldName --固定行高列宽避免图片挤压变形推荐尺寸参数内容类型行高(px)列宽(字符)证件照12015产品图20025二维码60102. 动态图片写入核心逻辑2.1 图片定位算法解析通过相对位置计算实现智能排版这段代码可处理多图片自动排列public WriteCellDataVoid calculateImagePosition(ListImageData images, int columns) { WriteCellDataVoid cellData new WriteCellData(); for (int i 0; i images.size(); i) { ImageData image images.get(i); int rowGroup i / columns; int colIndex i % columns; // 动态计算位置 image.setRelativeFirstRowIndex(rowGroup * 6); image.setRelativeLastRowIndex(rowGroup * 6 5); image.setRelativeFirstColumnIndex(colIndex * 4); image.setRelativeLastColumnIndex(colIndex * 4 3); } return cellData; }2.2 性能优化实践处理大批量图片时这三个技巧将执行时间缩短70%内存优化使用ByteArrayOutputStream缓冲图片数据并行处理对图片分组后使用并行流处理缓存机制重复图片只读取一次// 并行处理示例 ListCompletableFutureImageData futures imagePaths.stream() .parallel() .map(path - CompletableFuture.supplyAsync(() - loadImage(path))) .collect(Collectors.toList()); ListImageData images futures.stream() .map(CompletableFuture::join) .collect(Collectors.toList());3. Spring Boot集成方案3.1 控制器层最佳实践这个RESTful接口可直接生成并返回Excel流GetMapping(/export/employee-cards) public void exportEmployeeCards(HttpServletResponse response) throws IOException { response.setContentType(application/vnd.openxmlformats-officedocument.spreadsheetml.sheet); response.setHeader(Content-Disposition, attachment; filenameemployee_cards.xlsx); ListEmployee employees employeeService.listWithPhotos(); ExcelWriter writer EasyExcel.write(response.getOutputStream()) .withTemplate(ResourceUtils.getFile(classpath:templates/employee_card.xlsx)) .build(); employees.forEach(emp - { WriteSheet sheet EasyExcel.writerSheet(Page emp.getDept()).build(); writer.fill(convertToMap(emp), sheet); }); writer.finish(); }3.2 常见坑点解决方案这些报错你肯定遇到过图片变形设置固定宽高比imageData.setAnchor(new ClientAnchor(0, 0, 0, 0, col, row, col2, row4));内存溢出分批次写入并及时清理缓存格式错乱统一使用PNG格式图片4. 高级应用场景拓展4.1 混合内容报表生成结合文本、图片、条形码的复合报表解决方案使用MapString, Object混合不同类型数据分层填充策略先文本后图片动态调整模板样式MapString, Object data new HashMap(); data.put(title, 产品质检报告); data.put(items, productList); // 文本列表 data.put(signature, getSignatureImage()); // 签名图片 data.put(barcode, generateBarcode()); // 条形码4.2 云端文件处理方案当图片存储在OSS等云端服务时这样处理最优雅public ImageData loadFromOSS(String ossUrl) { OSS ossClient new OSSClientBuilder().build(endpoint, accessKey, secretKey); try (InputStream stream ossClient.getObject(bucketName, objectName).getObjectContent()) { ImageData image new ImageData(); image.setImage(IOUtils.toByteArray(stream)); return image; } finally { ossClient.shutdown(); } }报表生成后自动上传到云存储的完整流程本地生成临时文件使用断点续传分片上传清理本地临时文件返回下载链接String uploadId ossClient.initiateMultipartUpload(bucketName, objectName).getUploadId(); ListPartETag partETags new ArrayList(); // 分片上传逻辑... ossClient.completeMultipartUpload(new CompleteMultipartUploadRequest( bucketName, objectName, uploadId, partETags));
别再手动插图片了!用EasyExcel 3.x + POI动态生成带照片的Excel报表(附完整代码)
发布时间:2026/6/14 10:56:13
动态生成带图片的Excel报表EasyExcel 3.x实战指南每次手动在Excel里调整图片位置时我都忍不住想摔键盘——直到发现EasyExcel的模板填充功能。上周公司HR部门提出新需求要为300名新员工批量生成带照片的工牌Excel如果手动操作至少需要两天。而用下面这个方法我们只用了20分钟就完成了全部报表生成。1. 环境准备与模板设计1.1 依赖配置要点在Spring Boot项目中引入关键依赖时版本兼容性是个隐形杀手。推荐使用以下组合dependency groupIdcom.alibaba/groupId artifactIdeasyexcel/artifactId version3.1.1/version /dependency dependency groupIdorg.apache.poi/groupId artifactIdpoi/artifactId version5.2.3/version /dependency注意POI 4.x以上版本与EasyExcel 3.x存在图片写入兼容性问题实测5.2.3版本最稳定1.2 智能模板设计技巧设计模板时这些细节能让后续开发事半功倍使用${variable}作为文本占位符图片位置预留空白单元格并添加批注!-- image:fieldName --固定行高列宽避免图片挤压变形推荐尺寸参数内容类型行高(px)列宽(字符)证件照12015产品图20025二维码60102. 动态图片写入核心逻辑2.1 图片定位算法解析通过相对位置计算实现智能排版这段代码可处理多图片自动排列public WriteCellDataVoid calculateImagePosition(ListImageData images, int columns) { WriteCellDataVoid cellData new WriteCellData(); for (int i 0; i images.size(); i) { ImageData image images.get(i); int rowGroup i / columns; int colIndex i % columns; // 动态计算位置 image.setRelativeFirstRowIndex(rowGroup * 6); image.setRelativeLastRowIndex(rowGroup * 6 5); image.setRelativeFirstColumnIndex(colIndex * 4); image.setRelativeLastColumnIndex(colIndex * 4 3); } return cellData; }2.2 性能优化实践处理大批量图片时这三个技巧将执行时间缩短70%内存优化使用ByteArrayOutputStream缓冲图片数据并行处理对图片分组后使用并行流处理缓存机制重复图片只读取一次// 并行处理示例 ListCompletableFutureImageData futures imagePaths.stream() .parallel() .map(path - CompletableFuture.supplyAsync(() - loadImage(path))) .collect(Collectors.toList()); ListImageData images futures.stream() .map(CompletableFuture::join) .collect(Collectors.toList());3. Spring Boot集成方案3.1 控制器层最佳实践这个RESTful接口可直接生成并返回Excel流GetMapping(/export/employee-cards) public void exportEmployeeCards(HttpServletResponse response) throws IOException { response.setContentType(application/vnd.openxmlformats-officedocument.spreadsheetml.sheet); response.setHeader(Content-Disposition, attachment; filenameemployee_cards.xlsx); ListEmployee employees employeeService.listWithPhotos(); ExcelWriter writer EasyExcel.write(response.getOutputStream()) .withTemplate(ResourceUtils.getFile(classpath:templates/employee_card.xlsx)) .build(); employees.forEach(emp - { WriteSheet sheet EasyExcel.writerSheet(Page emp.getDept()).build(); writer.fill(convertToMap(emp), sheet); }); writer.finish(); }3.2 常见坑点解决方案这些报错你肯定遇到过图片变形设置固定宽高比imageData.setAnchor(new ClientAnchor(0, 0, 0, 0, col, row, col2, row4));内存溢出分批次写入并及时清理缓存格式错乱统一使用PNG格式图片4. 高级应用场景拓展4.1 混合内容报表生成结合文本、图片、条形码的复合报表解决方案使用MapString, Object混合不同类型数据分层填充策略先文本后图片动态调整模板样式MapString, Object data new HashMap(); data.put(title, 产品质检报告); data.put(items, productList); // 文本列表 data.put(signature, getSignatureImage()); // 签名图片 data.put(barcode, generateBarcode()); // 条形码4.2 云端文件处理方案当图片存储在OSS等云端服务时这样处理最优雅public ImageData loadFromOSS(String ossUrl) { OSS ossClient new OSSClientBuilder().build(endpoint, accessKey, secretKey); try (InputStream stream ossClient.getObject(bucketName, objectName).getObjectContent()) { ImageData image new ImageData(); image.setImage(IOUtils.toByteArray(stream)); return image; } finally { ossClient.shutdown(); } }报表生成后自动上传到云存储的完整流程本地生成临时文件使用断点续传分片上传清理本地临时文件返回下载链接String uploadId ossClient.initiateMultipartUpload(bucketName, objectName).getUploadId(); ListPartETag partETags new ArrayList(); // 分片上传逻辑... ossClient.completeMultipartUpload(new CompleteMultipartUploadRequest( bucketName, objectName, uploadId, partETags));