1

I use the following code which zip some folder to a given path,the issue that im currently facing is that I need to zip some folder with content into specific target and not in the same directory

For example

Folder in path like source

"/Users/i03434/go/src/zdf/BUILD"

target

"/Users/i03434/go/src/zdf/app/info.zip"

currently I try to add new path[2] which doesnt helps, any idea how to do it?

This is all the code

func zipit(params ...string) error {

    zipfile, err := os.Create(params[1])
    if err != nil {
        return err
    }
    defer zipfile.Close()

    archive := zip.NewWriter(zipfile)
    defer archive.Close()

    info, err := os.Stat(params[0])
    if err != nil {
        return err
    }

    var baseDir string
    if info.IsDir(); len(params) > 2 {
        baseDir = params[2]
    } else {
        baseDir = filepath.Base(params[0])
    }

    if baseDir != "" {
        baseDir += "/"
    }

    filepath.Walk(params[0], func(path string, info os.FileInfo, err error) error {
        if err != nil {
            return err
        }

        if info.IsDir() {
            return nil
        }

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

        if baseDir != "" {
            header.Name = filepath.Join(strings.TrimPrefix(path, baseDir))
        }

        header.Method = zip.Deflate

        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)
        return err
    })

    return err
}

The logic of the zip is working as expected, zip should be according to the jar spec

07_05_GuyT
  • 2,787
  • 14
  • 43
  • 88

1 Answers1

2

For Testing :

You can pass a mocked variable zipFile which impliments the io.Writer interface, as an argument for function zipit and compare it with expected data in a test.
Also you would need a known set of files in destination so that you compare it with the value in mock.

Refer this for testing io.Writer

Testing Code :

The string value of a zipped file created with specific values of source & newBaseDir should be known beforehand and stored in want.

func TestZipIt(t *testing.T) {

    source := ""
    newBaseDir := ""

    var zipFileMock bytes.Buffer
    if err := zipit(zipFileMock, source, newBaseDir); err != nil {
        t.Fatalf("zipit() returned an error: %s", err)
    }

    got := b.String()

    // want is the value of the zipped file as a string
    want := ...

    if got != want {
        t.Errorf("zipit() test failed")
        // t.Errorf("zipit() = %q, want %q", got, want)
    }
}

Program Code :

func main() {
    ...
    // params[0] is source
    // params[1] is destination
    // params[2] is newBaseDirectory

    zipfile, err := os.Create(destination)
    if err != nil {
        // handle error
    }
    defer zipfile.Close()

    if err = zipit(zipfile, source, newBaseDir); err != nil {
        // handle error
    }
    ...
}

func zipit(zipFile io.Writer, source, newBaseDir string) error {

    archive := zip.NewWriter(zipfile)
    defer archive.Close()

    info, err := os.Stat(source)
    if err != nil {
        return err
    }

    var baseDir string
    if info.IsDir() {
        baseDir = filepath.Dir(source)
    }

    if baseDir != "" {
        baseDir += "/"
    }

    filepath.Walk(source, func(path string, info os.FileInfo, err error) error {
        if err != nil {
            return err
        }

        if info.IsDir() {
            return nil
        }

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

        if baseDir != "" {
            header.Name = filepath.Join(newBaseDir, strings.TrimPrefix(path, baseDir))
        }

        header.Method = zip.Deflate

        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)
        return err
    })

    return err
}

Original Answer:

There are a few things to consider.

  • You need to use filepath.Dir instead of filepath.Base. Base gives the last element of the path not the base directory.

  • if info.IsDir(); len(params) > 2 { only checks the condition len(params) > 2, info.IsDir() is evaluated but not used anywhere.

Refer : If with a short statement format in Go [1] [2]

It should be

if info.IsDir() {
    if len(params) > 2 {
        ...
    } else {
        ...
    }
}
  • If my understanding of your requirement is right. The old base path needs to be stripped from the file headers and replaced with value of param[2] if available or assuming that we maintain a relative file structure, if it is empty.

Changes :

var baseDir string
if info.IsDir(); len(params) > 2 {
    baseDir = params[2]
} else {
    baseDir = filepath.Base(params[0])
}

should be

    var baseDir, newBaseDir string
    if info.IsDir() {
        baseDir = filepath.Dir(params[0])
        if len(params) > 2 {
            newBaseDir = params[2]
        }
    }

AND

header.Name = filepath.Join(strings.TrimPrefix(path, baseDir))
becomes
header.Name = filepath.Join(newBaseDir, strings.TrimPrefix(path, baseDir))

John S Perayil
  • 6,001
  • 1
  • 32
  • 47
  • Thanks :), But as requested I need unit test, could you please provide it? – 07_05_GuyT Jul 02 '18 at 19:43
  • @shopiaT I've updated the answer. Let me know if it's not clear enough. – John S Perayil Jul 05 '18 at 11:25
  • Can you please provide all the test , I want the E2E with the testing framework, I want to check it..thanks! – 07_05_GuyT Jul 05 '18 at 21:22
  • @shopiaT I have added a sample code for a test, but the values of source and newBaseDir as well as the string value of the zipped file created using those parameters need to be known beforehand for the test to be validated. – John S Perayil Jul 06 '18 at 12:57