尝试将内容从一个工作表复制到另一个工作表时,使用jxl api的ArrayIndexOutOfBoundException

时间:2011-12-06 12:55:56

标签: java xls jxl

每当我尝试使用模板创建xls时,使用jxl API运行到奇怪的ArrayIndexOutOfBoundException。 这是我尝试使用的代码片段

private static void readWorkSheet()throws Exception{
            File file = new File("C:\\reports\\");
            Map template = new HashMap();
            File[] listFiles = file.listFiles();
            for(File file1:listFiles){
                if(file1.getName().endsWith(".xls")){
                    System.out.println(" ==> "+file1.getName());
                    Workbook workbookTemplate = Workbook.getWorkbook(file1);
                    template.put(file1.getName(), workbookTemplate);
                }

            }
            System.out.println("template "+template);
            Workbook templateWorkBook = (Workbook)template.get("TestReport.xls");
            Sheet readSheet = templateWorkBook.getSheet("Sheet1");

            WritableWorkbook copy = Workbook.createWorkbook(new File("c://myfile_copy2.xls"));

            WritableSheet sheet = copy.createSheet("Test", 0);
            for (int i = 0; i < readSheet.getRows(); i++) {
                for (int j = 0; j < readSheet.getColumns(); j++) {
                    Cell readCell = readSheet.getCell(j, i);
                    CellFormat readFormat = readCell.getCellFormat();
                    if(readFormat != null && readCell.getContents() != null && readCell.getContents() != ""){
                        WritableCell newCell = new Label(i,j,readCell.getContents());
                        WritableCellFormat newFormat = new WritableCellFormat(readFormat);
                        newCell.setCellFormat(newFormat);
                        System.out.println("details of cell ["+i+", "+j+"]"+" Name = "+readCell.getContents());
                        System.out.println("details of newCell ["+i+", "+j+"]"+" Name = "+newCell.getContents());
                        sheet.addCell(newCell);
                    }
                }
        }
            copy.write();
            copy.close(); 
        }

不确定我在这里错过了什么!!!

异常我遇到了

  Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 56
    at jxl.biff.IndexMapping.getNewIndex(IndexMapping.java:68)
    at jxl.biff.XFRecord.rationalize(XFRecord.java:1667)
    at jxl.biff.FormattingRecords.rationalize(FormattingRecords.java:443)
    at jxl.write.biff.WritableWorkbookImpl.rationalize(WritableWorkbookImpl.java:1023)
    at jxl.write.biff.WritableWorkbookImpl.write(WritableWorkbookImpl.java:701)
    at com.jxl.test.JXLTest.readWorkSheet(JXLTest.java:83)
    at com.jxl.test.JXLTest.main(JXLTest.java:30)

3 个答案:

答案 0 :(得分:3)

我面临同样的问题。但只有当我尝试通过同一个类创建多个excel文件时才会发生这种情况。意味着如果我通过一个junit测试创建两个文件就会发生。如果我通过两个单独的单元测试类来完成这一切,一切正常。我只是在jxl代码中搜索一种强引用。
在那之前我找到了一个解决方法:

WorkbookSettings wbSettings = new WorkbookSettings();
wbSettings.setRationalization(false);

由于FormattingRecords.rationalize()方法而出现问题。上面的设置合理化过程的开关。 DateFormats和NumberFormats等将起作用你将通过这样做松开单元格格式的WritableFonts。

敬请关注 - 也许我会找到更多。

......我发现了更多: 主要问题是jxl.biff.Fonts类没有正确重新初始化,只包含4个默认的FontRecord实体。我挖得更深一点,发现在XFRecord类的initialize方法中有一个if语句:

if (!font.isInitialized())
{
   fonts.addFont(font);
}

第一次没有初始化字体,这里将成功添加。对于所有其他迭代,字体被初始化并且不会被添加。不知道谁持有XFRecords以及为什么它没有正确刷新。但我发现了一个有效的解决方法:

@SuppressWarnings("unchecked")
private void adjustUsedFonts(WritableWorkbook workbook) throws NoSuchFieldException, IllegalAccessException {
    WritableWorkbookImpl workbookImpl = (WritableWorkbookImpl) workbook;
    Field fontsField = WritableWorkbookImpl.class.getDeclaredField("fonts");
    fontsField.setAccessible(true);
    Fonts fonts = (Fonts) fontsField.get(workbookImpl);
    Field fontsListField = Fonts.class.getDeclaredField("fonts");
    fontsListField.setAccessible(true);
    ArrayList<FontRecord> fontRecords = (ArrayList<FontRecord>) fontsListField.get(fonts);
    // just to get to know which fonts are available
    for (FontRecord fontRecord : fontRecords) {
        logger.info("found font: name={}; size={}", fontRecord.getName(), fontRecord.getPointSize());
    }
    // DON'T DO THIS HARDCODED LIKE THIS 
    // BUT CHECK IF YOUR FONTS ARE AVAILABLE AND ADD IF NOT
    if (fontRecords.size() == 4) {
        logger.info("only 4 fonts available - add necessary ones");
        fontRecords.add(tableDefaultFont);
    }
}

我把这个方法放在调用workbook.write方法之前。并且它可以使用所有字体。 我知道这是一个黑客,没有好的解决方案。这就是为什么我会在他们的Ticket-Queue中打开一个问题(黑客只针对那些不能等到修复可用的人)。

我使用了这个版本的jxl,它似乎是最新的:
groupId:net.sourceforge.jexcelapi
artifactId:jxl
版本:2.6.12

此外: 如果您有多个自己的字体,您还需要使用其fontIndex以正确的顺序添加它。否则字体会混淆。我写了一个有用的方法来找出正确的索引:

    private void determineFontIndizesOfOwnFonts(WritableWorkbook workbook) throws NoSuchFieldException, IllegalAccessException {
    WritableWorkbookImpl workbookImpl = (WritableWorkbookImpl) workbook;
    Field frField = WritableWorkbookImpl.class.getDeclaredField("formatRecords");
    frField.setAccessible(true);
    FormattingRecords formRecords = (FormattingRecords) frField.get(workbookImpl);
    Field xfRecordListField = FormattingRecords.class.getDeclaredField("xfRecords");
    xfRecordListField.setAccessible(true);
    ArrayList<XFRecord> xfRecords = (ArrayList<XFRecord>) xfRecordListField.get(formRecords);
    logger.debug("amount of xfRecords: {}", xfRecords.size());
    for (XFRecord curXfRecord : xfRecords) {
        Font curFont = curXfRecord.getFont();
        if (curFont.equals(tableHeaderFont)) {
            logger.debug("font 'tableHeaderFont' info: idx={}, initialized={}, font[{}, {}px]", curXfRecord.getFontIndex(), curXfRecord.isInitialized(),
                    curFont.getName(), curFont.getPointSize());
            if (!fontIdxToName.containsKey(curXfRecord.getFontIndex())) {
                fontIdxToName.put(curXfRecord.getFontIndex(), tableHeaderFont);
            }
        }
        if (curFont.equals(tableContentFont)) {
            logger.debug("font 'tableContentFont' info: idx={}, initialized={}, font[{}, {}px]", curXfRecord.getFontIndex(), curXfRecord.isInitialized(),
                    curFont.getName(), curFont.getPointSize());
            if (!fontIdxToName.containsKey(curXfRecord.getFontIndex())) {
                fontIdxToName.put(curXfRecord.getFontIndex(), tableContentFont);
            }
        }
        if (curFont.equals(tableImportantOrFooterFont)) {
            logger.debug("font 'tableImportantOrFooterFont' info: idx={}, initialized={}, font[{}, {}px]", curXfRecord.getFontIndex(),
                    curXfRecord.isInitialized(), curFont.getName(), curFont.getPointSize());
            if (!fontIdxToName.containsKey(curXfRecord.getFontIndex())) {
                fontIdxToName.put(curXfRecord.getFontIndex(), tableImportantOrFooterFont);
            }
        }
    }
}

然后只需按正确的顺序添加,请参阅其他方法。

答案 1 :(得分:0)

我也遇到了同样的问题。我正在使用JXL库2.6.12。 我正在使用两个不同的“.xls”文件,并应用了3种不同的WritableCellFormat样式。当我在“.xls”单元格中使用相同的三个可写格式对象时,就会导致问题
解决方案:
我创建了一个方法,我在为WritableCellFormat创建不同的对象。我分别在“.xls”工作簿创建上调用了这个方法。这有效,两个“.xls”文件工作簿创建都是使用不同的可写单元格格式对象完成的。

答案 2 :(得分:0)

在2.4.2和2.6.12版本中,我也都遇到了这个问题。

我不再通过将任何Fonts / DisplayFormats / WritableCellFormats对象设为静态来解决此问题。

说句公道话,我确实在这里找到了解决方案: http://www.logikdev.com/2010/01/18/writablefont-doesnt-like-to-be-static/ 其中还引用了JExcelAPI常见问题解答:

  

来自http://jexcelapi.sourceforge.net/resources/faq/

     

”而且,请勿将单元格格式声明为静态也是很重要的。当单元格格式添加到工作表时,将为其分配一个内部索引号。如果您有两个线程(例如在Web环境中)编写两个不同的电子表格,则格式编号将变得混乱,结果工作表可能已损坏或不正确。”