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

EasyPoi:java导出excel,并从OSS下载附件打包zip,excel中每条记录用超链接关联附件目录

使用esaypoi生成Excel文件,并将附件打包成ZIP。Excel内的每个Sheet包含超链接,指向ZIP内的附件文件夹。程序首先创建Excel工作簿,设置表头,然后遍历数据,为每一项创建超链接到附件目录。

效果图

在这里插入图片描述

下载解压后的目录

在这里插入图片描述

注意:Window系统在解压缩软件内是打不开附件的,提示失败,需要解压缩后才可以正常打开

EasyPoi简介

⚠️ EasyPoi 已基本停止维护,新项目建议使用 Alibaba EasyExcel

在这里插入图片描述

Java代码实现

	/*** @Description: 导出政策材料以及附件* @author: ly* @param materialFileDto* @param response* @return void**/public void downloadMaterialFile(MaterialFileDto materialFileDto, HttpServletResponse response) throws Exception {List<MaterialFileDto> materialList = policyLibraryMapper.downloadMaterialFile(materialFileDto);List<MaterialFileDto> materialSampleList = policyLibraryMapper.downloadMaterialSample(materialFileDto);//创建临时目录:用于存放附件和生成 ZIPPath tempDir = Files.createTempDirectory("政策库");Path attachmentsDir = tempDir.resolve("附件目录");Files.createDirectory(attachmentsDir);//循环下载附件for (MaterialFileDto dto : materialSampleList) {downloadAndSetAttachment(dto, attachmentsDir);}for (MaterialFileDto dto : materialList) {downloadAndSetAttachment(dto, attachmentsDir);}// 创建 WorkbookWorkbook workbook = new XSSFWorkbook(); // 或 SXSSFWorkbook 用于大数据// 使用 ExcelExportService 支持多 sheetExcelExportService exportService = new ExcelExportService();// 第一个 Sheet:材料文件ExportParams exportParams1 = new ExportParams();exportParams1.setSheetName("材料表单");exportParams1.setType(ExcelType.XSSF);exportService.createSheet(workbook, exportParams1, MaterialFileDto.class, materialList);// 第二个 Sheet:材料样例ExportParams exportParams2 = new ExportParams();exportParams2.setSheetName("材料样例");exportParams2.setType(ExcelType.XSSF);exportService.createSheet(workbook, exportParams2, MaterialFileDto.class, materialSampleList);// 写入 Excel 到临时文件Path excelFile = tempDir.resolve("政策库信息.xlsx");try (FileOutputStream fos = new FileOutputStream(excelFile.toFile())) {workbook.write(fos);}//添加超链接addHyperlinksToExcel(excelFile, "附件目录");// 创建打包目录(Excel + 附件)Path zipFilePath = Files.createTempFile("政策库信息", ".zip");ZipCompressor zipCompressor = new ZipCompressor(zipFilePath.toString());zipCompressor.compressExe(tempDir.toString());ResponseUtils.downZip(response, "政策库信息.zip", zipFilePath);ZipCompressor.deleteFile(tempDir.toFile());ZipCompressor.deleteFile(zipFilePath.toFile());log.info("===============成功下载政策库附件=================");}/*** @Description: 手动添加超链接* 1、(isHyperlink = true 在 EasyPoi 4.x 及更早版本中存在严重 bug,即使字段有值也会报 空指针异常(NPE)目前最新的版本也没有修复)* 2、EasyPoi 没有标准 CellWriteHandler* 3、推荐使用阿里的easyExcel* @author: ly* @param excelFile* @param attachmentDirName* @return void**/private void addHyperlinksToExcel(Path excelFile, String attachmentDirName) throws IOException {try (FileInputStream fis = new FileInputStream(excelFile.toFile());Workbook workbook = WorkbookFactory.create(fis)) {CreationHelper helper = workbook.getCreationHelper();for (Sheet sheet : workbook) {for (Row row : sheet) {if (row.getRowNum() == 0) continue; // 跳过表头Cell cell = row.getCell(5); // 假设“附件目录”是第6列(索引5)if (cell == null) continue;String value = cell.getStringCellValue();if (value == null || value.isEmpty() ||value.startsWith("无附件") || value.contains("失败")) {continue;}// 创建超链接Hyperlink hyperlink = helper.createHyperlink(HyperlinkType.FILE);hyperlink.setAddress(value); // 相对路径cell.setHyperlink(hyperlink);// 设置样式CellStyle style = workbook.createCellStyle();Font font = workbook.createFont();font.setColor(IndexedColors.BLUE.getIndex());font.setUnderline(Font.U_SINGLE);style.setFont(font);// 设置居中对齐style.setAlignment(HorizontalAlignment.CENTER);        // 水平居中style.setVerticalAlignment(VerticalAlignment.CENTER);  // 垂直居中cell.setCellStyle(style);}}// 保存try (FileOutputStream fos = new FileOutputStream(excelFile.toFile())) {workbook.write(fos);}}}/*** @Description:  下载文件并设置 attachmentDir 为相对路径* @author: ly* @param dto* @param attachmentsDir* @return void**/private void downloadAndSetAttachment(MaterialFileDto dto, Path attachmentsDir) throws IOException {String ossPath = dto.getFilePath();if (ossPath != null && !ossPath.isEmpty()) {try {String fileName = Paths.get(ossPath).getFileName().toString();// 防止路径穿越if (fileName.contains("..")) {fileName = "invalid_file";}Path targetFile = attachmentsDir.resolve(fileName);// 调用阿里云 SDK 下载(示例伪代码)downloadFromOss(ossPath, targetFile.toFile(), fileName);// 设置相对路径(Windows 风格,Excel 更兼容)dto.setAttachmentDir("附件目录/" + fileName);} catch (Exception e) {// 处理异常,避免中断整个导出dto.setAttachmentDir("下载失败: ");}}}/*** @Description: 获取文件* @author: ly* @param filePath* @param targetFile* @param fileName* @return void**/private void downloadFromOss(String filePath, File targetFile, String fileName) {ossClientUtil.getZipFile(targetFile, filePath , fileName);}

工具类方法以及实体

public void getZipFile(File zipFilePath, String filePath, String fileName) {OSS ossClient = getOssClient();String fileUrl = getObjectKeyFromUrl(filePath);//String fName = getFileName(filePath);//String folder = FileUtils.getFileFolder(type);// OSSObject ossObject = ossClient.getObject(new GetObjectRequest(bucketName, folder+fileName));ossClient.getObject(new GetObjectRequest(bucketName, fileUrl), zipFilePath);ossClient.shutdown();
}public static String getObjectKeyFromUrl(String url) {try {URI uri = new URI(url);return uri.getPath().substring(1);} catch (Exception e) {throw new IllegalArgumentException("Invalid URL: " + url, e);}
}@Getter
@Setter
public class MaterialFileDto {private Long id;@Excel(name="城市",orderNum="1",width=10)private String cityName;@Excel(name="地区",orderNum="5",width=10)private String areaName;@Excel(name="服务类型",orderNum="8",width=15)private String businessName;@Excel(name="服务项目",orderNum="15",width=20)private String serviceName;@Excel(name="材料描述",orderNum="25",width=40, isWrap=true)private String material;@Excel(name = "附件目录",orderNum="35",width=40)private String attachmentDir="";private String filePath;private String fileName;}

到这里可能就会有同学有疑问,为什么不使用 easypoi 提供的便捷功能isHyperlink = true

  1. EasyPoi 确实支持通过注解 @Excel 的 isHyperlink属性来自动生成超链接单元格,能自动将字段渲染为超链接样式。

❌ isHyperlink = true 在 EasyPoi 4.x 及更早版本中存在严重 bug,即使字段有值也会报 空指针异常(NPE)

✅ 你遇到的问题是 EasyPoi 的已知缺陷,不是你的代码问题

为什么 isHyperlink = true 会空指针?EasyPoi 源码中处理超链接的逻辑如下:

if (isHyperlink) {Hyperlink link = workbook.getCreationHelper().createHyperlink(type);link.setAddress(obj.toString()); // obj 可能为 null → NPEcell.setHyperlink(link);
}
即使你打印 dto.getAttachmentDir() 有值,但在反射取值时可能为 null,导致 NPE。

在这里插入图片描述


文章转载自:

http://7bDQFGjE.nqbpz.cn
http://jiw3fCsa.nqbpz.cn
http://nDdYlxid.nqbpz.cn
http://O4O8xr34.nqbpz.cn
http://D2jpdGZw.nqbpz.cn
http://f4PwuMh5.nqbpz.cn
http://PcjTqkQH.nqbpz.cn
http://W6yg1kH0.nqbpz.cn
http://RTmQf6V5.nqbpz.cn
http://VBm1afXo.nqbpz.cn
http://eMN0Z5Ia.nqbpz.cn
http://2YjZxGVv.nqbpz.cn
http://bNxC04ra.nqbpz.cn
http://HrKpuQQY.nqbpz.cn
http://BkSFBja7.nqbpz.cn
http://lboPHLhK.nqbpz.cn
http://u6fBWQci.nqbpz.cn
http://So0irMM0.nqbpz.cn
http://t913RH4V.nqbpz.cn
http://1IGVY2zh.nqbpz.cn
http://Pf1uyovL.nqbpz.cn
http://rzuQ6nD0.nqbpz.cn
http://6uosEpeM.nqbpz.cn
http://C3gUG3Sm.nqbpz.cn
http://Sbodm4BC.nqbpz.cn
http://9qkOK2BC.nqbpz.cn
http://1ik1j0zH.nqbpz.cn
http://dqfziy2a.nqbpz.cn
http://0oIcjqVJ.nqbpz.cn
http://cAkXe1pD.nqbpz.cn
http://www.dtcms.com/a/384356.html

相关文章:

  • Win10系统下载并安装声卡驱动
  • JavaEE初阶——初识计算机是如何工作的:从逻辑门到现代操作系统
  • CKA05--service
  • 信息安全专业毕业设计选题推荐:课题建议与开题指导
  • 【LeetCode 每日一题】1792. 最大平均通过率——贪心 + 优先队列
  • 【深度学习计算机视觉】05:多尺度目标检测
  • Docker将镜像搬移到其他服务上的方法
  • WiseAI-百度研发的AI智能聊天产品
  • .NET驾驭Word之力:理解Word对象模型核心 (Application, Document, Range)
  • 【JAVA接口自动化】JAVA如何读取Yaml文件
  • Redis全面指南:从入门到精通
  • Word在WPS和Office中给图片添加黑色边框
  • C++ Lua组合拳:构建高性能系统配置管理框架
  • 数据库编程--完成简单的信息登录系统+思维导图
  • Spring Boot 深入剖析:SpringApplicationRunListener
  • 【新手指南】解析Laf.run上的GET API接口
  • 如何批量删除 iPhone/iPad 上的照片 [7 种方法
  • Spring Boot 日志体系全面解析:从 SLF4J 到 Logback、Log4j2 与 Lombok 超详细!!
  • springboot创建请求处理
  • 08-Redis 字符串类型全解析:从命令实操到业务场景落地
  • 学习海康VisionMaster之字符缺陷检测
  • CAD画图:002软件界面操作
  • 解锁全球业务潜能:AWS全球网络加速解决方案深度解析
  • HTTPS Everywhere 是什么?HTTPS 插件作用、iOS 抓包失败原因解析与常见抓包工具对比
  • 【C++】STL详解(七)—stack和queue的介绍及使用
  • 20250912在荣品RD-RK3588-MID开发板的Android13系统下拿掉卡迪屏的reset引脚的下拉复位波形
  • 在线图书借阅平台的设计与实现 —— 基于飞算JavaAI的实战开发全流程与优化实践
  • Git : 分支管理和远程仓库
  • 当传统金融遇上AI智能:AIStock系统深度技术解析
  • 大数据如何捕捉你的爱好?如何实现跨站用户行为分析?