解决easyExcel按模板导出xlsx文件打开提示“发现xxx.xlsx中部分内容有问题,是否让我们尽量尝试恢复?”的问题

这篇具有很好参考价值的文章主要介绍了解决easyExcel按模板导出xlsx文件打开提示“发现xxx.xlsx中部分内容有问题,是否让我们尽量尝试恢复?”的问题。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

最近项目一个需求要求将订单按照excel模板导出,其中商品有多行,需要动态插入行并且存在合并单元格的情况,使用easyExcel官网提供的demo的填充和合并单元格:
官网填充demo
官网合并单元格demo

按模板导出主要代码:

public class DataToExcel {
	public void exportFile() {
		File filePath = new File("D:\\test\\testMerge.xlsx");
		OutputStream os= Files.newOutputStream(filePath.toPath());
		
		int firstRow = 18;  //从第18行开始合并
		int lastRow = 18;
		int beginRow = 18;
		
		//单元格合并
		List<CellRangeAddress> cellRangeAddressList = new ArrayList<>();
		if (CollectionUtil.isNotEmpty(excelVoList)) {
			if (excelVoList.size() > 1) {
				for (int i = 0; i < excelVoList.size() - 1; i++) {
					cellRangeAddressList.add(new CellRangeAddress(firstRow, lastRow, 1, 4));
					cellRangeAddressList.add(new CellRangeAddress(firstRow, lastRow, 7, 8));
					firstRow++;
					lastRow++;
				}
			}
		}
		
		FillMergeStrategy fillMergeStrategy = new FillMergeStrategy(cellRangeAddressList, beginRow, excelVoList.size() - 1);
		
		//获取excel模板
		File file = new File("D:\\template\\template01.xlsx");
		InputStream inputStream = Files.newInputStream(file.toPath());
		//InputStream inputStream = new URL(filePath).openStream();
		
		ExcelWriter excelWriter = EasyExcel.write(os).withTemplate(inputStream)
		        .registerWriteHandler(fillMergeStrategy)
				.build();
		WriteSheet writeSheet = EasyExcel.writerSheet().build();
		FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
		
		//参数集合,直接写入到Excel数据
		excelWriter.fill(paramsMap, writeSheet);
		//列表数据
		excelWriter.fill(excelVoList, fillConfig, writeSheet);
		excelWriter.finish();
	}
}

合并单元格的策略为:

public class PiFillMergeStrategy implements RowWriteHandler {

    //合并坐标集合
    private List<CellRangeAddress> cellRangeAddress;
    //从哪行开始
    private int beginRow;
    //合并行数
    private int mergeRows;

    public PiFillMergeStrategy(List<CellRangeAddress> cellRangeAddress, int beginRow, int mergeRows) {
        this.cellRangeAddress = cellRangeAddress;
        this.beginRow = beginRow;
        this.mergeRows = mergeRows;
    }

    @Override
    public void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Integer relativeRowIndex, Boolean isHead) {
        if (CollectionUtil.isNotEmpty(cellRangeAddress)) {
            if (row.getRowNum() >= beginRow && row.getRowNum() <= beginRow + mergeRows) {
                for (CellRangeAddress item : cellRangeAddress) {
                    writeSheetHolder.getSheet().addMergedRegionUnsafe(item);
                }
            }
        }
    }

}

当有多行商品导出的excel文件打开时会提示:
easyexcel导出文件损坏,java
点击“是”是可以打开的,但用户体验很不好,认为导出文件有问题!
调试了下easyExcel代码,发现合并单元格的方法主要有两个:

    /**
	  * 添加单元格的合并区域(因此这些单元格形成一个)
	  * 参数:region – (rowfrom/colfrom-rowto/colto) 合并
	  * 返回:该地区的指数
     */
    int addMergedRegion(CellRangeAddress region);

    /**
     * 添加单元格的合并区域(因此这些单元格形成一个)。跳过验证。可以创建重叠的合并区域或创建与多单元格
     * 数组公式与此公式相交的合并区域,这可能会导致工作簿损坏。要在调用 addMergedRegionUnsafe 后检
     * 查合并区域重叠数组公式或其他合并区域,请调用validateMergedRegions() ,它在 O(n^2) 时间内
     * 运行。
     * 参数:region ——合并
	 * 返回:该地区的指数
	 * 抛出:IllegalArgumentException – 如果区域包含的单元格少于 2 个
     */
    int addMergedRegionUnsafe(CellRangeAddress region);

可以看出使用addMergedRegionUnsafe方法合并单元格可能会导致工作簿损坏,而使用addMergedRegion会进行单元格是否重复合并的校验:

private int addMergedRegion(CellRangeAddress region, boolean validate) {
        if (region.getNumberOfCells() < 2) {
            throw new IllegalArgumentException("Merged region " + region.formatAsString() + " must contain 2 or more cells");
        }
        region.validate(SpreadsheetVersion.EXCEL2007);

        if (validate) {
            // throw IllegalStateException if the argument CellRangeAddress intersects with
            // a multi-cell array formula defined in this sheet
            validateArrayFormulas(region);

            // Throw IllegalStateException if the argument CellRangeAddress intersects with
            // a merged region already in this sheet
            validateMergedRegions(region);
        }

        CTMergeCells ctMergeCells = worksheet.isSetMergeCells() ? worksheet.getMergeCells() : worksheet.addNewMergeCells();
        CTMergeCell ctMergeCell = ctMergeCells.addNewMergeCell();
        ctMergeCell.setRef(region.formatAsString());
        final int numMergeRegions=ctMergeCells.sizeOfMergeCellArray();

        // also adjust the number of merged regions overall
        ctMergeCells.setCount(numMergeRegions);

        return numMergeRegions-1;
    }

校验合并单元格的方法validateMergedRegions(region),如果候选区域不与此工作表中的现有合并区域相交就会报错:

private void validateMergedRegions(CellRangeAddress candidateRegion) {
        for (final CellRangeAddress existingRegion : getMergedRegions()) {
            if (existingRegion.intersects(candidateRegion)) {
                throw new IllegalStateException("Cannot add merged region " + candidateRegion.formatAsString() +
                        " to sheet because it overlaps with an existing merged region (" + existingRegion.formatAsString() + ").");
            }
        }
    }

可以看出addMergedRegionUnsafe会跳过单元格合并的校验,但会导致文件被损坏,所以导出的文件打开后会提示文件有问题,如果使用addMergedRegion方法,easyExcel在列表动态添加行excelWriter.fill(excelVoList, fillConfig, writeSheet);时就会直接报上述错误,导致程序中断。

我采用的方法是用easyExcel不使用合并策略导出xlsx文件到临时文件中,然后使用poi的XSSFWorkbook读取该临时文件,然后用这个新的临时文件进行单元格合并,这样单元格检查时就不会报错了,顺利导出,打开后也不会有错误提示!

public class DataToExcel {
	public void exportFile() {
		File filePath = new File("D:\\test\\testMerge.xlsx");
		OutputStream os= Files.newOutputStream(filePath.toPath());
		
		int firstRow = 18;  //从第18行开始合并
		int lastRow = 18;
		int beginRow = 18;
		
		//单元格合并
		List<CellRangeAddress> cellRangeAddressList = new ArrayList<>();
		if (CollectionUtil.isNotEmpty(excelVoList)) {
			if (excelVoList.size() > 1) {
				for (int i = 0; i < excelVoList.size() - 1; i++) {
					cellRangeAddressList.add(new CellRangeAddress(firstRow, lastRow, 1, 4));
					cellRangeAddressList.add(new CellRangeAddress(firstRow, lastRow, 7, 8));
					firstRow++;
					lastRow++;
				}
			}
		}
		
		//将easyExcel生成的文件保存在临时文件中待poi进一步做合并单元格
		File tmpFile = new File("D:\\tmp\\tmpFile.xlsx");
		OutputStream tmpOutputStream = Files.newOutputStream(tmpFile.toPath());
		//获取excel模板
		File file = new File("D:\\template\\template01.xlsx");
		InputStream inputStream = Files.newInputStream(file.toPath());
		
		//将easyExcel生成的文件保存在临时文件中待poi进一步做合并单元格
		//File tmpFile = new File("/tmp/" + "tmp_file.xlsx");
		//OutputStream tmpOutputStream = Files.newOutputStream(tmpFile.toPath());
		//获取excel模板
		//InputStream inputStream = new URL(filePath).openStream();
		
		ExcelWriter excelWriter = EasyExcel.write(tmpOutputStream).withTemplate(inputStream)
		//      .registerWriteHandler(fillMergeStrategy)  //不采用合并策略
				.build();
		WriteSheet writeSheet = EasyExcel.writerSheet().build();
		FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
		
		//参数集合,直接写入到Excel数据
		excelWriter.fill(paramsMap, writeSheet);
		//列表数据
		excelWriter.fill(excelVoList, fillConfig, writeSheet);
		excelWriter.finish();
		
		//使用poi合并单元格,使用registerWriteHandler合并单元格会与fill方法中创建单元格后校验合并单元格冲突而引发报错
		InputStream in = Files.newInputStream(tmpFile.toPath());
		XSSFWorkbook workbook = new XSSFWorkbook(in);
		XSSFSheet sheet = workbook.getSheetAt(0);
		if (CollectionUtils.isNotEmpty(cellRangeAddressList)) {
			for (CellRangeAddress cellAddresses : cellRangeAddressList) {
				//合并单元格
				sheet.addMergedRegion(cellAddresses);
				//设置单元格样式,解决合并单元格后边框缺失问题
				setRegionStyle(sheet, cellAddresses, setDefaultStyle(workbook));
			}
		}
		workbook.write(os);
		os.flush();
		os.close();
	}
	
	//使用poi设置合并单元格后的样式
	public void setRegionStyle(XSSFSheet sheet, CellRangeAddress region, XSSFCellStyle xssfCellStyle) {
		for (int i = region.getFirstRow(); i <= region.getLastRow(); i++) {
			XSSFRow row = sheet.getRow(i);
			if (null == row) row = sheet.createRow(i);
			for (int j = region.getFirstColumn(); j <= region.getLastColumn(); j++) {
				XSSFCell cell = row.getCell(j);
				if (null == cell) cell = row.createCell(j);
				cell.setCellStyle(xssfCellStyle);
			}
		}
	}
	
	public XSSFCellStyle setDefaultStyle(XSSFWorkbook workbook) {
		XSSFCellStyle cellStyle = workbook.createCellStyle();
		// 边框
		cellStyle.setBorderBottom(BorderStyle.THIN);
		cellStyle.setBorderLeft(BorderStyle.THIN);
		cellStyle.setBorderRight(BorderStyle.THIN);
		cellStyle.setBorderTop(BorderStyle.THIN);
		// 居中
		cellStyle.setAlignment(HorizontalAlignment.CENTER);
		cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
		// 字体
		XSSFFont font = workbook.createFont();
		font.setFontName("Calibri");
		font.setFontHeightInPoints((short) 10);
		cellStyle.setFont(font);
		return cellStyle;
	}
}

如果有更好的解决方式,欢迎再评论区留言哦!

参考文章来源地址https://www.toymoban.com/news/detail-641455.html

到了这里,关于解决easyExcel按模板导出xlsx文件打开提示“发现xxx.xlsx中部分内容有问题,是否让我们尽量尝试恢复?”的问题的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Java结合EasyExcel,模板文件填充并导出Excel】

    需求描述: 客户网页上填一个Excel表格,数据存到数据库,这个导出接口要做的就是从数据库中的获取数据并填充到模板文件,最后通过response返给前端一个下载链接,用户即可获取填充好的Excel文件。 方案一: 一开始使用的是easypoi,发现当填充一行数据时是OK的,但是如果

    2024年02月09日
    浏览(56)
  • easyexcel根据模板导出Excel文件,表格自动填充问题

    同事在做easyexcel导出Excel,根据模板导出的时候,发现导出的表格,总会覆盖落款的内容。 这就很尴尬了,表格居然不能自动填充,直接怒喷工具,哈哈。 然后一起看了一下这个问题。 我找了自己的系统中关于表格导出的页面,导出了一下,发现可以正常扩充。 于是排查问

    2024年02月06日
    浏览(44)
  • Excel无法打开文件新建 XLSX 工作表.xlsx,因为文件格式或文件扩展名无效。请确定文件未损坏解决办法【笔记】

    使用问题: 右键新建Microsoft Excel工作表,双击打开表格文件提示以下内容: “Excel无法打开文件新建 XLSX 工作表.xlsx,因为文件格式或文件扩展名无效。请确定文件未损坏,并且文件扩展名与文件的格式匹配” 。 确认了以下路径的文件正常打开 C:Program FilesMicrosoft Officero

    2024年02月11日
    浏览(71)
  • word转化为ftl格式文件模板,导出后office提示文件错误

    使用模板,导出word文件,最近在做这个需求,本地环境用的是wps,结合本地的环境快速完成了开发需求之后,有一天客户发现office打开报错,本人深感不接,wps都能打开,各个在线文档也都支持,为何office就不支持,环境不同。 wps是按照office版本迭代开发,照理说office是w

    2024年02月12日
    浏览(46)
  • EasyExcel导出工具类(支持模板导出多Sheet导出)

    最近写需求发现没有顺手excel导出工具类, 于是自己造了个小轮子, 链式一\\\".\\\"到底, 代码既注释 特点: 支持多sheet 支持从模板导出 支持分页拿数据, 避免数据量过大一次拿出导致内存溢出 数据类型支持业务实体类或Map, 无需easyExcel的注解, 低侵入   over

    2024年02月16日
    浏览(43)
  • python中使用pandas 导出到excel ,打开excel有错误,错误的提示为:发现“***”中的部分内容问题,是否让我们尽量尝试修复?如果您信任此工作簿的源,请单击“是”。

    目录  问题及解决办法 发现问题出现的原因为:文件重复保存  解决问题的方法为:注释掉writer.save() 完美解决!!!生成的表格打开便不会再有警告提示。 pd.ExcelWriter() 是 Pandas 库中的一个函数,用于创建一个 Excel 文件的写入器(Excel writer)对象,可以用来将数据写入 Ex

    2024年02月06日
    浏览(48)
  • POI及EasyExcel操作xls,xlsx文件

    Apache POI 是基于 Office Open XML 标准(OOXML)和 Microsoft 的 OLE 2 复合文档格式(OLE2)处理各种文件格式的开源项目。 可以使用 Java 读写 MS Excel 文件,可以使用 Java 读写 MS Word 和 MS PowerPoint 文件。 HSSF - 提供读写 Microsoft Excel XLS 格式 (Microsoft Excel 97 (-2003)) 档案的功能。 XSSF - 提

    2024年02月11日
    浏览(38)
  • easyexcel poi根据模板导出Excel

    参考:https://blog.csdn.net/weixin_45742032/article/details/119593288?spm=1001.2101.3001.6650.1utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-1-119593288-blog-86538258.235%5Ev38%5Epc_relevant_anti_t3_basedepth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-1-11959328

    2024年02月10日
    浏览(43)
  • 使用easyexcel填充模板数据,并导出excel

    导出excel功能非常场景,本片文章记录如何使用模板填充数据后再导出。因直接导出excel数据样式不符合要求,所以做了模板填充然后再导出excel。 效果如下: 注意:列表数据变量名前面要写点{.id},如果单条数据可以不写。 使用表单提交: 实体代码: controller代码: 只对je

    2024年03月11日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包