A part with the name ‘/xl/worksheets/sheet1.xml‘ already exists : Packages shall not contain equival
一. 异常报告java.util.concurrent.ExecutionException: org.apache.poi.openxml4j.exceptions.PartAlreadyExistsException:A part with the name '/xl/worksheets/sheet1.xml' already exists : Packages shall notconta
·
一. 异常报告
java.util.concurrent.ExecutionException: org.apache.poi.openxml4j.exceptions.PartAlreadyExistsException:
A part with the name '/xl/worksheets/sheet1.xml' already exists : Packages shall not
contain equivalent part names and package implementers shall neither create nor recognize
packages with equivalent part names. [M1.12]
at java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.util.concurrent.FutureTask.get(FutureTask.java:192)
at com.xlcloud.business.vip.util.parallel.ParallelQueryExecutor.execute(ParallelQueryExecutor.java:62)
at com.xlcloud.business.vip.service.orders.NewExportGoodsOldOrderService.newExportGoodsOrderList(NewExportGoodsOldOrderService.java:54)
at com.xlcloud.business.vip.service.orders.NewExportGoodsOldOrderService$$FastClassBySpringCGLIB$$cf8ecc61.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:746)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.aop.interceptor.AsyncExecutionInterceptor.lambda$invoke$0(AsyncExecutionInterceptor.java:115)
at org.springframework.aop.interceptor.AsyncExecutionInterceptor$$Lambda$714/372457144.call(Unknown Source)
at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266)
at java.util.concurrent.FutureTask.run(FutureTask.java)
at java.lang.Thread.run(Thread.java:745)
Caused by: org.apache.poi.openxml4j.exceptions.PartAlreadyExistsException: A part with the name '/xl/worksheets/sheet1.xml' already exists : Packages shall not contain equivalent part names and package implementers shall neither create nor recognize packages with equivalent part names. [M1.12]
at org.apache.poi.openxml4j.opc.OPCPackage.createPart(OPCPackage.java:889)
at org.apache.poi.openxml4j.opc.OPCPackage.createPart(OPCPackage.java:853)
at org.apache.poi.POIXMLDocumentPart.createRelationship(POIXMLDocumentPart.java:558)
at org.apache.poi.xssf.usermodel.XSSFWorkbook.createSheet(XSSFWorkbook.java:858)
at org.apache.poi.xssf.streaming.SXSSFWorkbook.createSheet(SXSSFWorkbook.java:677)
at com.xlcloud.business.vip.service.orders.PayOrderNewOldTask.orderAreaExcel(PayOrderNewOldTask.java:52)
at com.xlcloud.business.vip.service.orders.PayOrderNewOldTask.results(PayOrderNewOldTask.java:41)
at com.xlcloud.business.vip.util.parallel.ParallelTask.call(ParallelTask.java:20)
at com.xlcloud.business.vip.util.parallel.ParallelTask.call(ParallelTask.java:10)
at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266)
at java.util.concurrent.FutureTask.run(FutureTask.java)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
... 1 common frames omitted
二. 产生背景和解决方案
问题背景: 使用多线程绘制excel表格,每个线程创建一个sheet,测试服偶现上面的异常信息,且频率较高。看源码也没发现什么问题,传入的sheet名称确定是完全不同的。一时间没有定位问题。
在后来,觉得是多线程绘制表格产生的问题。再次看源码,发现问题:
1)报错位置
PackagePart createPart(PackagePartName partName, String contentType,
boolean loadRelationships) {
throwExceptionIfReadOnly();
if (partName == null) {
throw new IllegalArgumentException("partName");
}
if (contentType == null || contentType.equals("")) {
throw new IllegalArgumentException("contentType");
}
// Check if the specified part name already exists
if (partList.containsKey(partName)
&& !partList.get(partName).isDeleted()) {
throw new PartAlreadyExistsException(
"A part with the name '" + partName.getName() + "'" +
" already exists : Packages shall not contain equivalent part names and package" +
" implementers shall neither create nor recognize packages with equivalent part names. [M1.12]");
}
.......
}
2)向上查找
由报错位置可知partName重复了,继续往上找
protected final RelationPart createRelationship(POIXMLRelation descriptor, POIXMLFactory factory, int idx, boolean noRelation){
try {
PackagePartName ppName = PackagingURIHelper.createPartName(descriptor.getFileName(idx));
PackageRelationship rel = null;
PackagePart part = packagePart.getPackage().createPart(ppName, descriptor.getContentType());
......
}
}
由上可知 partName 是由 descriptor 中根据idx获取的,idx可能在多线程中传入的是相同的
public XSSFSheet createSheet(String sheetname) {
......
int sheetNumber = 1;
outerloop:
while(true) {
for(XSSFSheet sh : sheets) {
sheetNumber = (int)Math.max(sh.sheet.getSheetId() + 1, sheetNumber);
}
// Bug 57165: We also need to check that the resulting file name is not already taken
// this can happen when moving/cloning sheets
String sheetName = XSSFRelation.WORKSHEET.getFileName(sheetNumber);
for(POIXMLDocumentPart relation : getRelations()) {
if(relation.getPackagePart() != null &&
sheetName.equals(relation.getPackagePart().getPartName().getName())) {
// name is taken => try next one
sheetNumber++;
continue outerloop;
}
}
// no duplicate found => use this one
break;
}
RelationPart rp = createRelationship(XSSFRelation.WORKSHEET, XSSFFactory.getInstance(), sheetNumber, false);
......
}
可以看出,当前方法线程不安全,多线程下同一个Workbook对象可能产生相同的sheetNumber,从而导致文中最上面产生的问题。
三. 解决方法
在创建sheet时,加同步锁
SXSSFSheet sheet;
synchronized (ExportOrder.class){
sheet=wb.createSheet(goodsOrderRequestDto.getSheetName());
}
更多推荐
已为社区贡献1条内容
所有评论(0)