Repository: qax-os/excelize (Go)

Issue:
Temp files may be left
### Description

Temp files may be left if some of them is removed before Close.

### Steps to reproduce the issue

1. Open an xlsx
2. Remove one of the created $TMPDIR/excelize-* files
3. Close the opened xlsx

### Describe the results you received

Depending on closing order, some of the temp files may be left.

### Describe the results you expected

No /tmp/excelize-* files left

### Go version

1.24.1

### Excelize version or commit ID

2.9.0

### Environment

```shell
Debian Linux testing
```

### Validations

- [x] Check that there isn't already an issue that reports the same bug to avoid creating a duplicate.
- [x] The provided reproduction is a minimal reproducible example of the bug.


Discussion:
Thanks for your issue. This may caused by [this line](https://github.com/xuri/excelize/blob/3ccbdaa9cc8c0f0c94f0445da3a3097c19fd2d78/file.go#L98), needs replace return true instead of false to resolve this bug. If you like to create a pull request for this, that's would be great.
> Thanks for your issue. This may caused by [this line](https://github.com/xuri/excelize/blob/3ccbdaa9cc8c0f0c94f0445da3a3097c19fd2d78/file.go#L98), needs replace return true instead of false to resolve this bug. 

Exactly.

> If you like to create a pull request for this, that's would be great.

First I've started creating the PR (#2120), then I've seen that it needs a bug, so that's why we're here (#2119) :-)




Patch:
diff --git a/file.go b/file.go
index c5ccff53db..26ad3504e8 100644
--- a/file.go
+++ b/file.go
@@ -87,22 +87,23 @@ func (f *File) SaveAs(name string, opts ...Options) error {
 
 // Close closes and cleanup the open temporary file for the spreadsheet.
 func (f *File) Close() error {
-	var err error
+	var firstErr error
 	if f.sharedStringTemp != nil {
-		if err := f.sharedStringTemp.Close(); err != nil {
-			return err
-		}
+		firstErr = f.sharedStringTemp.Close()
+		f.sharedStringTemp = nil
+	}
+	for _, stream := range f.streams {
+		_ = stream.rawData.Close()
 	}
+	f.streams = nil
 	f.tempFiles.Range(func(k, v interface{}) bool {
-		if err = os.Remove(v.(string)); err != nil {
-			return false
+		if err := os.Remove(v.(string)); err != nil && firstErr == nil {
+			firstErr = err
 		}
 		return true
 	})
-	for _, stream := range f.streams {
-		_ = stream.rawData.Close()
-	}
-	return err
+	f.tempFiles.Clear()
+	return firstErr
 }
 
 // Write provides a function to write to an io.Writer.
diff --git a/lib.go b/lib.go
index e06e7f5105..113d574fbf 100644
--- a/lib.go
+++ b/lib.go
@@ -80,7 +80,7 @@ func (f *File) ReadZipReader(r *zip.Reader) (map[string][]byte, int, error) {
 // unzipToTemp unzip the zip entity to the system temporary directory and
 // returned the unzipped file path.
 func (f *File) unzipToTemp(zipFile *zip.File) (string, error) {
-	tmp, err := os.CreateTemp(os.TempDir(), "excelize-")
+	tmp, err := os.CreateTemp("", "excelize-")
 	if err != nil {
 		return "", err
 	}
diff --git a/rows.go b/rows.go
index 986eb4502d..fca3c68b20 100644
--- a/rows.go
+++ b/rows.go
@@ -139,8 +139,10 @@ func (rows *Rows) Error() error {
 // Close closes the open worksheet XML file in the system temporary
 // directory.
 func (rows *Rows) Close() error {
-	if rows.tempFile != nil {
-		return rows.tempFile.Close()
+	tempFile := rows.tempFile
+	rows.tempFile = nil
+	if tempFile != nil {
+		return tempFile.Close()
 	}
 	return nil
 }
@@ -366,7 +368,7 @@ func (f *File) getFromStringItem(index int) string {
 		}()
 	}
 	f.sharedStringItem = [][]uint{}
-	f.sharedStringTemp, _ = os.CreateTemp(os.TempDir(), "excelize-")
+	f.sharedStringTemp, _ = os.CreateTemp("", "excelize-")
 	f.tempFiles.Store(defaultTempFileSST, f.sharedStringTemp.Name())
 	var (
 		inElement string
diff --git a/stream.go b/stream.go
index 392d046bde..63309ff3bd 100644
--- a/stream.go
+++ b/stream.go
@@ -775,7 +775,7 @@ func (bw *bufferedWriter) Sync() (err error) {
 		return nil
 	}
 	if bw.tmp == nil {
-		bw.tmp, err = os.CreateTemp(os.TempDir(), "excelize-")
+		bw.tmp, err = os.CreateTemp("", "excelize-")
 		if err != nil {
 			// can not use local storage
 			return nil


Test patch:
diff --git a/file_test.go b/file_test.go
index 4a9b5eaa22..58c9e4a265 100644
--- a/file_test.go
+++ b/file_test.go
@@ -7,6 +7,7 @@ import (
 	"math"
 	"os"
 	"path/filepath"
+	"strconv"
 	"strings"
 	"sync"
 	"testing"
@@ -164,3 +165,24 @@ func TestZip64(t *testing.T) {
 		assert.NoError(t, f.Close())
 	})
 }
+
+func TestRemoveTempFiles(t *testing.T) {
+	tmp, err := os.CreateTemp("", "excelize-*")
+	if err != nil {
+		t.Fatal(err)
+	}
+	tmpName := tmp.Name()
+	tmp.Close()
+	f := NewFile()
+	// fill the tempFiles map with non-existing (erroring on Remove) "files"
+	for i := 0; i < 1000; i++ {
+		f.tempFiles.Store(strconv.Itoa(i), "/hopefully not existing")
+	}
+	f.tempFiles.Store("existing", tmpName)
+
+	require.Error(t, f.Close())
+	if _, err := os.Stat(tmpName); err == nil {
+		t.Errorf("temp file %q still exist", tmpName)
+		os.Remove(tmpName)
+	}
+}