diff --git a/api/next/54898.txt b/api/next/54898.txt new file mode 100644 index 00000000000000..44133bd37794e9 --- /dev/null +++ b/api/next/54898.txt @@ -0,0 +1 @@ +pkg archive/zip, method (*Writer) AddFS(fs.FS) error #54898 diff --git a/src/archive/zip/writer.go b/src/archive/zip/writer.go index 3b23cc3391d9e6..0e81c6a5d7c713 100644 --- a/src/archive/zip/writer.go +++ b/src/archive/zip/writer.go @@ -11,6 +11,7 @@ import ( "hash" "hash/crc32" "io" + "io/fs" "strings" "unicode/utf8" ) @@ -495,6 +496,41 @@ func (w *Writer) RegisterCompressor(method uint16, comp Compressor) { w.compressors[method] = comp } +// AddFS adds the files from fs.FS to the archive. +// It walks the directory tree starting at the root of the filesystem +// adding each file to the zip using deflate while maintaining the directory structure. +func (w *Writer) AddFS(fsys fs.FS) error { + return fs.WalkDir(fsys, ".", func(name string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + if d.IsDir() { + return nil + } + info, err := d.Info() + if err != nil { + return err + } + h, err := FileInfoHeader(info) + if err != nil { + return err + } + h.Name = name + h.Method = Deflate + fw, err := w.CreateHeader(h) + if err != nil { + return err + } + f, err := fsys.Open(name) + if err != nil { + return err + } + defer f.Close() + _, err = io.Copy(fw, f) + return err + }) +} + func (w *Writer) compressor(method uint16) Compressor { comp := w.compressors[method] if comp == nil { diff --git a/src/archive/zip/writer_test.go b/src/archive/zip/writer_test.go index 2b73eca814f621..5250bc112ee0e4 100644 --- a/src/archive/zip/writer_test.go +++ b/src/archive/zip/writer_test.go @@ -16,6 +16,7 @@ import ( "os" "strings" "testing" + "testing/fstest" "time" ) @@ -602,3 +603,48 @@ func BenchmarkCompressedZipGarbage(b *testing.B) { } }) } + +func writeTestsToFS(tests []WriteTest) fs.FS { + fsys := fstest.MapFS{} + for _, wt := range tests { + fsys[wt.Name] = &fstest.MapFile{ + Data: wt.Data, + Mode: wt.Mode, + } + } + return fsys +} + +func TestWriterAddFS(t *testing.T) { + buf := new(bytes.Buffer) + w := NewWriter(buf) + tests := []WriteTest{ + { + Name: "file.go", + Data: []byte("hello"), + Mode: 0644, + }, + { + Name: "subfolder/another.go", + Data: []byte("world"), + Mode: 0644, + }, + } + err := w.AddFS(writeTestsToFS(tests)) + if err != nil { + t.Fatal(err) + } + + if err := w.Close(); err != nil { + t.Fatal(err) + } + + // read it back + r, err := NewReader(bytes.NewReader(buf.Bytes()), int64(buf.Len())) + if err != nil { + t.Fatal(err) + } + for i, wt := range tests { + testReadFile(t, r.File[i], &wt) + } +}