当前位置: 首页 > 知识库问答 >
问题:

用Apache Poi重命名XSSFTable头会导致XLSX文件损坏

曾翰飞
2023-03-14

我正在尝试重命名现有XLSX文件的标题。这个想法是使用一个excel文件将数据从XML导出到excel,并在某个用户进行调整后重新导入XML。

目前,我们已经用Excel创建了一个“模板”xlsx-sheet,其中已经包含了一个可排序表(poi中的XSSFTable)和一个到XSD-Source的映射。然后通过POI进行导入,将XML数据映射到其中并保存。为了根据用户调整工作表,我们希望将现有表的标题/列名转换为不同的语言。它与POI3.10-final一起工作,但自从升级到4.0.1后,打开时会导致损坏的XLSX文件。

我在stackoverflow上发现了这个问题,当我更改标题(列标题)中任何单元格的值时,Excel文件就会损坏,但这个问题没有得到回答,而且相当古老。但是我试图弄清楚这些注释可能是关于什么的,并试图将现有的XSSFTable扁平化,将填充的数据复制到一个新的表中,并将一个新的XSSFTable添加到数据中。可悲的是,这似乎是相当复杂的,所以我回到纠正破碎的标题单元格。我还试图用POI创建整个工作表,并逐步避免使用“模板”-XSLX,但我不知道如何实现我们的XSD-映射(在Excel中,它的开发人员-tools->source->Add,然后将节点映射到动态表中的一些单元格)

在poi升级之前工作的代码基本上是这样的:

java prettyprint-override">//Sheet is the current XSSFSheet
//header is a Map with the original header-name from the template mapped to a the new translated name
//headerrownumber is the row containing the tableheader to be translated

 public static void translateHeaders(Sheet sheet,final Map<String,String> header,int headerrownumber) {
  CellRangeAddress address = new CellRangeAddress(headerrownumber,headerrownumber,0,sheet.getRow(headerrownumber).getLastCellNum());  //Cellrange is the header-row

        MyCellWalk cellWalk = new MyCellWalk (sheet,address);
        cellWalk.traverse(new CellHandler() {
            public void onCell(Cell cell, CellWalkContext ctx) {
                String val = cell.getStringCellValue();
                if (header.containsKey(val)) {
                    cell.setCellValue(header.get(val));
                }
            }
        });
}

MyCellWalk是一个org.apache.poi.ss.util.CellWalk.CellWalk,它从左上角到右下角遍历单元格范围。

就我所知,仅仅更改单元格的平面值是不够的,因为xlsx在它们的一些映射中保留了对cellname的引用,但我无法找出如何获取所有这些引用并重命名标题。也许还有另一种方法来翻译headernames?

共有1个答案

壤驷穆冉
2023-03-14

如果Apache POI不会失败,xssftable.updateHeader应该可以完成这个任务。

以下所有操作都是使用Apache poi 4.0.1完成的。

我已经下载了dummy_template.xlsx,然后尝试更改工作表中的表列标题。但是,即使在调用xssftable.updateHeaders之后,xssftable中的列名也没有改变。因此,我查看了xssftable.java->updateHeaders以确定为什么没有发生这种情况。在那里我们发现:

if (row != null && row.getCTRow().validate()) {
 //do changing the column names
}
<row r="4" spans="1:3" x14ac:dyDescent="0.25">

注意附加的x14ac:dydescent属性。这就是为什么row.getctrow().validate()返回false

下面的代码获取您的dummy_template.xlsx,重命名工作表中的列标题,然后调用解除的版本Static void updateHeaders(XSSFTable表)。之后,result.xlsx对于在Excel中打开有效。

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.*;
import org.apache.poi.ss.util.cellwalk.*;

import org.apache.poi.xssf.usermodel.*;

import org.openxmlformats.schemas.spreadsheetml.x2006.main.*;

import java.io.*;
import java.util.*;

class ExcelRenameTableColumns {

 static void translateHeaders(Sheet sheet, final Map<String,String> header, int headerrownumber) {
  CellRangeAddress address = new CellRangeAddress(
   headerrownumber, headerrownumber, 
   0, sheet.getRow(headerrownumber).getLastCellNum());

  CellWalk cellWalk = new CellWalk (sheet, address);
  cellWalk.traverse(new CellHandler() {
   public void onCell(Cell cell, CellWalkContext ctx) {
    String val = cell.getStringCellValue();
    if (header.containsKey(val)) {
     cell.setCellValue(header.get(val));
    }
   }
  });
 }

 static void updateHeaders(XSSFTable table) {
  XSSFSheet sheet = (XSSFSheet)table.getParent();
  CellReference ref = table.getStartCellReference();

  if (ref == null) return;

  int headerRow = ref.getRow();
  int firstHeaderColumn = ref.getCol();
  XSSFRow row = sheet.getRow(headerRow);
  DataFormatter formatter = new DataFormatter();

System.out.println(row.getCTRow().validate()); // false!

  if (row != null /*&& row.getCTRow().validate()*/) {
   int cellnum = firstHeaderColumn;
   CTTableColumns ctTableColumns = table.getCTTable().getTableColumns();
   if(ctTableColumns != null) {
    for (CTTableColumn col : ctTableColumns.getTableColumnList()) {
     XSSFCell cell = row.getCell(cellnum);
     if (cell != null) {
      col.setName(formatter.formatCellValue(cell));
     }
     cellnum++;
    }
   }
  }
 }

 public static void main(String[] args) throws Exception {

  String templatePath = "dummy_template.xlsx";
  String outputPath = "result.xlsx";

  FileInputStream inputStream = new FileInputStream(templatePath);
  Workbook workbook = WorkbookFactory.create(inputStream);
  Sheet sheet = workbook.getSheetAt(0);

  Map<String, String> header = new HashMap<String, String>();
  header.put("textone", "Spalte eins");
  header.put("texttwo", "Spalte zwei");
  header.put("textthree", "Spalte drei");

  translateHeaders(sheet, header, 3);

  XSSFTable table = ((XSSFSheet)sheet).getTables().get(0);

  updateHeaders(table);

  FileOutputStream outputStream = new FileOutputStream(outputPath);
  workbook.write(outputStream);
  outputStream.close();
  workbook.close();

 }
}

如果使用Excel 2007打开dummy_template.xlsx,然后另存为dummy_template2007.xlsx,则该行的xml更改为

<行r=“4”spans=“1:3”>

现在,在使用此dummy_template2007.xlsx时,不需要手动调用xssftable.updateheaders。由xssftable.commit调用的xssftable.writeto自动执行此操作。

 类似资料:
  • 问题内容: 我以前使用Axios下载GET端点提供的文件。端点已更改,现在是POST,但是不需要参数。我正在更新原始的下载方法,但是返回了损坏的文件。 我不知道,如果问题出在,或如何响应的处理或全部的上方。到目前为止,我已经尝试了各种选择,但没有运气。任何建议将不胜感激! 我已经能够使用Postman下载文件,所以我知道端点提供的文件很好。我只是无法在我的React代码中理清参数来做到这一点。 问

  • 我正在编写一个概念的演示证明,将电子签名添加到现有的pdf中。我遇到了一个奇怪的问题,我不明白。似乎将签名添加到某些文档可以正常工作,而将签名添加到其他文档则不行,并且会生成Adobe Reader无法打开的损坏文件。 这是我的代码: 请看这里的文件。Infile1.pdf我不能签名,但Infile2.pdf签名很好。Outfile1.pdf是损坏的文件。https://app.box.com/s

  • 我正在使用ApachePOI,我创建了一个XSSF工作簿,并尝试打开一个xlsx文件。它在当地的一个地方很有效。但是,当我用Excel打开真正服务器(AWS EC2、Tomcat8、JDK 1.8)上的Excel文件时,它显示文件已损坏(.xls工作)。这是我的代码: 本地Spring4, jdk1.8, tomcat 8.0, maven 真正的AWS EC2亚马逊linux2,jdk1。8、t

  • 文件重命名 文件重命名 源码/* * Copyright (c) 2006-2018, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * *//* * 程序清单:更改名称 * * 程序会创建一个操作文件的函数并导出到msh命令列表 * 在

  • 我有一段代码,目前正在写入一个

  • Docx4J生成的Excel工作簿总是说损坏了,但我无法确定Excel不喜欢底层XML的什么,更不用说如何修复它了。 我的用例如下:我试图定期自动生成一个带有图表和图形的excel工作簿。只有原始数据会改变,但随着原始数据的改变,其他一切都会动态更新。 null null 在我的空白工作簿之前和之后 欢迎所有的想法。