Cretae ZIP file with directories inside

Hello folks,

I’m trying to write a small program that compress a directory using ZIP.
here the code

package main

import (
	"archive/zip"
	"fmt"
	"io"
	"io/fs"
	"log"
	"os"
	"path/filepath"
	"time"
)

var (
	writer *zip.Writer
)

func main() {
	fmt.Println("Hello from Dir Walker")

	args := os.Args[1:]
	if len(args) < 1 {
		log.Fatal("Missing input arguments")
	}

	f, err := os.Create(args[1])
	if err != nil {
		log.Fatal("Error:", err)
	}
	defer f.Close()

	writer = zip.NewWriter(f)
	defer writer.Close()

	start := time.Now()
	if err := filepath.WalkDir(args[0], walker); err != nil {
		log.Printf("Error in walker: %s\n", err)
	}
	t := time.Now()
	elapsed := t.Sub(start)
	fmt.Printf("Elapsed time: %v\n", elapsed)
}

func walker(path string, d fs.DirEntry, err error) error {
	if err != nil {
		return err
	}

	info, err := d.Info()
	if err != nil {
		return err
	}

	header, err := zip.FileInfoHeader(info)
	if err != nil {
		return err
	}

	header.Method = zip.Deflate
	header.Name = path

	hw, err := writer.CreateHeader(header)
	if err != nil {
		return err
	}

	if d.IsDir() || (d.Type()&fs.ModeSymlink != 0) {
		return nil
	}

	f, err := os.Open(path)
	if err != nil {
		return err
	}
	defer f.Close()

	_, err = io.Copy(hw, f)
	return err
}

the code works well if the directory contains only files.
When it contains also subdirectories I get error when I try to uncompress form command line

» unzip ../output.zip                                                                               massimoc@massimo-mbp
Archive:  ../output.zip
  inflating: _                       
  inflating: a                       
checkdir error:  a exists but is not directory
                 unable to process a/b.
checkdir error:  a exists but is not directory
                 unable to process a/b/c.
checkdir error:  a exists but is not directory
                 unable to process a/b/c/d.
checkdir error:  a exists but is not directory
                 unable to process a/b/c/d/test.txt.
  inflating: cmd                     
checkdir error:  cmd exists but is not directory
                 unable to process cmd/zipper.
checkdir error:  cmd exists but is not directory
                 unable to process cmd/zipper/main.go.
  inflating: go.mod                  
  inflating: walk_dir                
  inflating: walk_dir.go             

looks like the directory are added but are not marked as directory.

I’m pretty new on Go so I’m pretty sure is something stupid I’m missing

I found the solution, added

if d.IsDir() {
    header.Name += "/"
}

The Create() documentation says

To create a directory instead of a file, add a trailing slash to the name.

Bu the CreateHeader() has no mention about it

1 Like