diff --git a/fileutil/file.go b/fileutil/file.go index 173aed0..8b0f1f6 100644 --- a/fileutil/file.go +++ b/fileutil/file.go @@ -208,7 +208,11 @@ func Zip(fpath string, destPath string) error { archive := zip.NewWriter(zipFile) defer archive.Close() - err = filepath.Walk(fpath, func(path string, info os.FileInfo, err error) error { + return addFileToArchive(fpath, archive) +} + +func addFileToArchive(fpath string, archive *zip.Writer) error { + err := filepath.Walk(fpath, func(path string, info os.FileInfo, err error) error { if err != nil { return err } @@ -224,32 +228,22 @@ func Zip(fpath string, destPath string) error { header.Name += "/" } else { header.Method = zip.Deflate - } - - writer, err := archive.CreateHeader(header) - if err != nil { - return err - } - - if !info.IsDir() { + writer, err := archive.CreateHeader(header) + if err != nil { + return err + } file, err := os.Open(path) if err != nil { return err } defer file.Close() - _, err = io.Copy(writer, file) - if err != nil { + if _, err := io.Copy(writer, file); err != nil { return err } } return nil }) - - if err != nil { - return err - } - - return nil + return err } // UnZip unzip the file and save it to destPath. @@ -302,6 +296,64 @@ func UnZip(zipFile string, destPath string) error { return nil } +// ZipAppendEntry append a single file or directory by fpath to an existing zip file. +// Play: https://go.dev/play/p/cxvaT8TRNQp +func ZipAppendEntry(fpath string, destPath string) error { + tempFile, err := os.CreateTemp("", "temp.zip") + if err != nil { + return err + } + defer os.Remove(tempFile.Name()) + + zipReader, err := zip.OpenReader(destPath) + if err != nil { + return err + } + + archive := zip.NewWriter(tempFile) + + for _, zipItem := range zipReader.File { + zipItemReader, err := zipItem.Open() + if err != nil { + return err + } + header, err := zip.FileInfoHeader(zipItem.FileInfo()) + if err != nil { + return err + } + header.Name = zipItem.Name + targetItem, err := archive.CreateHeader(header) + if err != nil { + return err + } + _, err = io.Copy(targetItem, zipItemReader) + if err != nil { + return err + } + } + + err = addFileToArchive(fpath, archive) + + if err != nil { + return err + } + + err = zipReader.Close() + if err != nil { + return err + } + err = archive.Close() + if err != nil { + return err + } + err = tempFile.Close() + if err != nil { + return err + } + + return CopyFile(tempFile.Name(), destPath) +} + func safeFilepathJoin(path1, path2 string) (string, error) { relPath, err := filepath.Rel(".", path2) if err != nil || strings.HasPrefix(relPath, "..") { diff --git a/fileutil/file_example_test.go b/fileutil/file_example_test.go index 79e30bf..d8927cf 100644 --- a/fileutil/file_example_test.go +++ b/fileutil/file_example_test.go @@ -224,6 +224,30 @@ func ExampleUnZip() { // application/octet-stream } +func ExampleZipAppendEntry() { + zipFile := "./test.zip" + CopyFile("./testdata/file.go.zip", zipFile) + + ZipAppendEntry("./testdata", zipFile) + + unZipPath := "./unzip" + UnZip(zipFile, unZipPath) + + fmt.Println(IsExist("./unzip/file.go")) + fmt.Println(IsExist("./unzip/testdata/file.go.zip")) + fmt.Println(IsExist("./unzip/testdata/test.csv")) + fmt.Println(IsExist("./unzip/testdata/test.txt")) + + os.Remove(zipFile) + os.RemoveAll(unZipPath) + + // Output: + // true + // true + // true + // true +} + func ExampleIsZipFile() { result1 := IsZipFile("./file.go") result2 := IsZipFile("./testdata/file.go.zip") diff --git a/fileutil/file_test.go b/fileutil/file_test.go index 6f64f09..5074080 100644 --- a/fileutil/file_test.go +++ b/fileutil/file_test.go @@ -191,6 +191,45 @@ func TestZipAndUnZip(t *testing.T) { os.RemoveAll(unZipPath) } +func TestZipAppendEntry(t *testing.T) { + assert := internal.NewAssert(t, "TestZipAppendEntry") + + zipFile := "./text.zip" + err := CopyFile("./testdata/file.go.zip", zipFile) + assert.IsNil(err) + + srcFile := "./text.txt" + CreateFile(srcFile) + + file, _ := os.OpenFile(srcFile, os.O_WRONLY|os.O_TRUNC, os.ModePerm) + + _, err = file.WriteString("hello\nworld") + if err != nil { + t.Fail() + } + file.Close() + + err = ZipAppendEntry(srcFile, zipFile) + assert.IsNil(err) + + err = ZipAppendEntry("./testdata", zipFile) + assert.IsNil(err) + + unZipPath := "./unzip" + err = UnZip(zipFile, unZipPath) + assert.IsNil(err) + + assert.Equal(true, IsExist("./unzip/text.txt")) + assert.Equal(true, IsExist("./unzip/file.go")) + assert.Equal(true, IsExist("./unzip/testdata/file.go.zip")) + assert.Equal(true, IsExist("./unzip/testdata/test.csv")) + assert.Equal(true, IsExist("./unzip/testdata/test.txt")) + + os.Remove(srcFile) + os.Remove(zipFile) + os.RemoveAll(unZipPath) +} + func TestFileMode(t *testing.T) { assert := internal.NewAssert(t, "TestFileMode")