This is an automated email from the ASF dual-hosted git repository.
delei pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fesod.git
The following commit(s) were added to refs/heads/main by this push:
new d973557c fix: resolve Windows fill template copy failures (#1021)
(#1034)
d973557c is described below
commit d973557ce2587d2e5fa43da2d5d73b4cf024d2a6
Author: Mikkey-f <[email protected]>
AuthorDate: Mon Aug 24 12:51:11 2026 +0800
fix: resolve Windows fill template copy failures (#1021) (#1034)
* fix: close output stream when WriteWorkbookHolder init fails (#1021)
The constructor opens the output file stream before copying the template.
If initialization fails (e.g. missing template), the stream was never
closed, leaving the target file locked on Windows. Close streams opened
by the holder on failure; caller-provided streams remain the caller's
responsibility.
* fix: decode percent-encoded resource URLs in ExampleFileUtil (#1021)
URL.getPath() returns the encoded path. On Windows, template file names
built with File.separator ('\') are percent-encoded as %5C in the
resource URL, so the returned path pointed at a non-existent file and
fill examples failed with 'Copy template failure'. Decode via
URL.toURI() instead.
* fix: honor autoCloseStream when closing output stream on init failure
(#1021)
---------
Co-authored-by: Mikkey-f <[email protected]>
Co-authored-by: DeleiGuo <[email protected]>
---
.../fesod/sheet/examples/util/ExampleFileUtil.java | 21 ++++++-
.../write/metadata/holder/WriteWorkbookHolder.java | 23 +++++++-
.../WriteWorkbookHolderOutputStreamTest.java | 67 ++++++++++++++++++++++
3 files changed, 107 insertions(+), 4 deletions(-)
diff --git
a/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/util/ExampleFileUtil.java
b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/util/ExampleFileUtil.java
index 0bfbc75d..000f5927 100644
---
a/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/util/ExampleFileUtil.java
+++
b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/util/ExampleFileUtil.java
@@ -54,7 +54,7 @@ public class ExampleFileUtil {
if (resource == null) {
throw new IllegalStateException("Cannot find classpath root
resource");
}
- return resource.getPath();
+ return toFilePath(resource);
}
/**
@@ -66,12 +66,29 @@ public class ExampleFileUtil {
public static String getExamplePath(String fileName) {
java.net.URL resource =
ExampleFileUtil.class.getClassLoader().getResource(EXAMPLE + "/" + fileName);
if (resource != null) {
- return resource.getPath();
+ return toFilePath(resource);
}
// Fallback to classpath root + example path
return getPath() + EXAMPLE + File.separator + fileName;
}
+ /**
+ * Convert a resource URL to a file path, decoding percent-encoded
characters.
+ * <p>
+ * {@link java.net.URL#getPath()} returns the encoded path (e.g. {@code
%5C} for a backslash on Windows),
+ * which is not usable with {@link java.io.File}. {@link
java.net.URL#toURI()} decodes it correctly.
+ *
+ * @param resource the resource URL to convert
+ * @return the absolute file path
+ */
+ private static String toFilePath(java.net.URL resource) {
+ try {
+ return new File(resource.toURI()).getAbsolutePath();
+ } catch (java.net.URISyntaxException e) {
+ throw new IllegalStateException("Invalid resource URL: " +
resource, e);
+ }
+ }
+
/**
* Get the path to write output files in the system temp directory.
*
diff --git
a/fesod-sheet/src/main/java/org/apache/fesod/sheet/write/metadata/holder/WriteWorkbookHolder.java
b/fesod-sheet/src/main/java/org/apache/fesod/sheet/write/metadata/holder/WriteWorkbookHolder.java
index 7750e80a..021dd852 100644
---
a/fesod-sheet/src/main/java/org/apache/fesod/sheet/write/metadata/holder/WriteWorkbookHolder.java
+++
b/fesod-sheet/src/main/java/org/apache/fesod/sheet/write/metadata/holder/WriteWorkbookHolder.java
@@ -240,12 +240,15 @@ public class WriteWorkbookHolder extends
AbstractWriteHolder {
}
// init handler
- initHandler(writeWorkbook, null);
-
try {
+ initHandler(writeWorkbook, null);
copyTemplate();
} catch (IOException e) {
+ closeOutputStreamOnFailure();
throw new ExcelGenerateException("Copy template failure.", e);
+ } catch (RuntimeException e) {
+ closeOutputStreamOnFailure();
+ throw e;
}
if (writeWorkbook.getMandatoryUseInputStream() == null) {
this.mandatoryUseInputStream = Boolean.FALSE;
@@ -270,6 +273,22 @@ public class WriteWorkbookHolder extends
AbstractWriteHolder {
this.dataFormatMap = MapUtils.newHashMap();
}
+ /**
+ * Close the output stream when initialization fails, so that the target
file is not held open.
+ * Mirrors the close behavior of the success path, which is also gated by
{@code autoCloseStream};
+ * callers that opt out of automatic closing via {@code
autoCloseStream(false)} keep that behavior
+ * here as well.
+ */
+ private void closeOutputStreamOnFailure() {
+ if (autoCloseStream && outputStream != null) {
+ try {
+ outputStream.close();
+ } catch (IOException e) {
+ log.warn("Failed to close output stream after workbook
initialization failure.", e);
+ }
+ }
+ }
+
private void copyTemplate() throws IOException {
if (writeWorkbook.getTemplateFile() == null &&
writeWorkbook.getTemplateInputStream() == null) {
return;
diff --git
a/fesod-sheet/src/test/java/org/apache/fesod/sheet/write/metadata/holder/WriteWorkbookHolderOutputStreamTest.java
b/fesod-sheet/src/test/java/org/apache/fesod/sheet/write/metadata/holder/WriteWorkbookHolderOutputStreamTest.java
new file mode 100644
index 00000000..b39c56c3
--- /dev/null
+++
b/fesod-sheet/src/test/java/org/apache/fesod/sheet/write/metadata/holder/WriteWorkbookHolderOutputStreamTest.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.fesod.sheet.write.metadata.holder;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Path;
+import org.apache.fesod.sheet.exception.ExcelGenerateException;
+import org.apache.fesod.sheet.write.metadata.WriteWorkbook;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+/**
+ * Tests for the output stream lifecycle of {@link WriteWorkbookHolder} when
workbook
+ * initialization fails.
+ */
+public class WriteWorkbookHolderOutputStreamTest {
+
+ @TempDir
+ Path tempDir;
+
+ @Test
+ void constructorClosesOutputFileStreamWhenTemplateCopyFails() throws
IOException {
+ File outputFile = tempDir.resolve("output.xlsx").toFile();
+ WriteWorkbook writeWorkbook = new WriteWorkbook();
+ writeWorkbook.setFile(outputFile);
+
writeWorkbook.setTemplateFile(tempDir.resolve("missing-template.xlsx").toFile());
+
+ Assertions.assertThrows(ExcelGenerateException.class, () -> new
WriteWorkbookHolder(writeWorkbook));
+ // On Windows an open FileOutputStream locks the file, so deletion
fails if the holder
+ // leaked the stream it opened. On Unix the deletion succeeds either
way, keeping the
+ // assertion portable.
+ Assertions.assertTrue(outputFile.delete(), "output stream should be
closed after initialization failure");
+ }
+
+ @Test
+ void
constructorDoesNotCloseCallerProvidedOutputStreamWhenAutoCloseDisabled() throws
IOException {
+ WriteWorkbook writeWorkbook = new WriteWorkbook();
+ writeWorkbook.setAutoCloseStream(false);
+ writeWorkbook.setOutputStream(new ByteArrayOutputStream());
+
writeWorkbook.setTemplateFile(tempDir.resolve("missing-template.xlsx").toFile());
+
+ Assertions.assertThrows(ExcelGenerateException.class, () -> new
WriteWorkbookHolder(writeWorkbook));
+ // autoCloseStream(false) opts into manual stream management, so the
holder must not close
+ // caller-provided streams — mirroring the success-path contract.
+ Assertions.assertDoesNotThrow(() ->
writeWorkbook.getOutputStream().write(1));
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]