You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何使用XSSFWorkbook会修改原始Excel模板文件?

问题

在Jira Scriptrunner中用Groovy开发,借助Apache POI的XSSFWorkbook处理.xlsx模板文件。脚本仅在内存中修改工作簿,调用workbook.write(outputStream)和workbook.close()后,并未主动以原文件名保存到服务器,但服务器上的原始模板文件却被意外修改。

原代码如下:

import org.apache.poi.xssf.usermodel.XSSFWorkbook
import releasemanagement.changes.AttachmentCreator
import other.TMPConfig
import com.atlassian.jira.issue.fields.CustomField
import com.atlassian.jira.issue.CustomFieldManager
import com.atlassian.jira.component.ComponentAccessor
import org.apache.poi.xssf.usermodel.XSSFCell

CustomFieldManager custFldManag = ComponentAccessor.getCustomFieldManager()

def issue = Issues.getByKey("ISSUEKEY")

def attachmentCreator = new AttachmentCreator()
def attachmentManager = ComponentAccessor.getAttachmentManager()

def wb = new XSSFWorkbook(new File("path/Template.xlsx"))

CustomField currCF
XSSFCell currCell
String reportType = "Initial"

TMPConfig.fieldToCell.each{k, v ->
    List<Integer> cellCoordinates = v.get(reportType)
    if(cellCoordinates){
        currCell = wb.getSheetAt(cellCoordinates[0]).getRow(cellCoordinates[1]).getCell(cellCoordinates[2])
        if(currCell){
            currCF = custFldManag.getCustomFieldObject(k)
            currCell.setCellValue(currCF.getValue(issue).toString())
        }
    }
}

ByteArrayOutputStream out = new ByteArrayOutputStream()
wb.write(out)
wb.close()
ByteArrayInputStream inputStream = new ByteArrayInputStream(out.toByteArray())
out.close()

int filename_num = 1
String filename = "Dora_${reportType}_${filename_num}.xlsx"

attachmentCreator.createAttachmentForIssue(issue, filename, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", inputStream)

同事的临时解决代码:

XSSFWorkbook wb_temp = new XSSFWorkbook(new File("path/Template.xlsx"))
ByteArrayOutputStream out_temp = new ByteArrayOutputStream()
wb_temp.write(out_temp)
wb_temp.close()
ByteArrayInputStream inputStream_temp = new ByteArrayInputStream(out_temp.toByteArray())
out_temp.close()
XSSFWorkbook wb = new XSSFWorkbook(inputStream_temp)

原因分析

这是Apache POI中XSSFWorkbook的默认行为导致的:

  • 当直接用File对象构造XSSFWorkbook时,POI为优化性能,会采用随机访问文件模式(底层依赖RandomAccessFile),而非一次性将整个文件加载到内存。
  • 这种模式下,工作簿修改过程中,部分中间数据可能被写回原始File对应的磁盘文件,尤其是调用close()方法时,POI会执行清理或同步操作,最终导致原始模板文件被篡改。
  • 你以为只是在内存中修改,但POI的底层实现让工作簿始终和原始文件句柄保持关联,修改意外渗透到了磁盘文件。

临时方案的原理

同事的代码通过内存中转切断了工作簿与原始文件的关联:

  1. 先将原始模板加载到临时工作簿wb_temp,再把它写入ByteArrayOutputStream(完全在内存中完成)。
  2. 关闭临时工作簿,释放与原始文件的关联。
  3. 从内存字节流构造新的wb工作簿,此时该工作簿完全基于内存数据,和磁盘上的原始模板文件无任何关联。
  4. 后续修改这个内存工作簿,自然不会影响服务器上的原始模板文件。

内容的提问来源于stack exchange,提问作者TryingToPassCompSci

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 14:22:07