easy way to unzip file with golang

后端 未结 8 770
情话喂你
情话喂你 2021-02-01 03:45

is there a easy way to unzip file with golang ?

right now my code is:

func Unzip(src, dest string) error {
    r, err := zip.OpenReader(src)
    if err !         


        
相关标签:
8条回答
  • 2021-02-01 04:06

    I have been doing some browsing of google and repeatedly found people saying that there is no library that can handle that. Maybe I missed a custom repository in my search though and someone else will find it for us.

    You may be able to make use of io.Copy(src, dest) to ease the process but I haven't tested it at all.

    For instance:

    os.MkDirAll(dest, r.File.Mode)
    d, _ := os.Open(dest)
    io.Copy(r.File, d)
    

    Honestly to me your code looks pretty nice and if I were to do an Extract function myself (and the above doesn't work) then I would probably take a page from your book.

    0 讨论(0)
  • 2021-02-01 04:09

    I would prefer using 7zip with Go, which would give you something like this.

    func extractZip() {
        fmt.Println("extracting", zip_path)
        commandString := fmt.Sprintf(`7za e %s %s`, zip_path, dest_path)
        commandSlice := strings.Fields(commandString)
        fmt.Println(commandString)
        c := exec.Command(commandSlice[0], commandSlice[1:]...)
        e := c.Run()
        checkError(e)
    }
    

    Better example code

    However, if using 7zip isn't possible, then try this. Defer a recover to catch the panics. (Example)

    func checkError(e error){
      if e != nil {
        panic(e)
      }
    }
    func cloneZipItem(f *zip.File, dest string){
        // Create full directory path
        path := filepath.Join(dest, f.Name)
        fmt.Println("Creating", path)
        err := os.MkdirAll(filepath.Dir(path), os.ModeDir|os.ModePerm)
        checkError(err)
    
        // Clone if item is a file
        rc, err := f.Open()
        checkError(err)
        if !f.FileInfo().IsDir() {
            // Use os.Create() since Zip don't store file permissions.
            fileCopy, err := os.Create(path)
            checkError(err)
            _, err = io.Copy(fileCopy, rc)
            fileCopy.Close()
            checkError(err)
        }
        rc.Close()
    }
    func Extract(zip_path, dest string) {
        r, err := zip.OpenReader(zip_path)
        checkError(err)
        defer r.Close()
        for _, f := range r.File {
            cloneZipItem(f, dest)
        }
    }
    
    0 讨论(0)
  • 2021-02-01 04:13

    While working on the query for catching ZipSlip vulnerabilities in Go on LGTM.com (of which I am a developer), I noticed code similar to the accepted answer in several projects, e.g. rclone.

    As @woogoo has pointed out, this code is vulnerable to ZipSlip, so I believe the answer should be updated to something like the following (code taken from rclone fix):

    func Unzip(src, dest string) error {
        dest = filepath.Clean(dest) + string(os.PathSeparator)
    
        r, err := zip.OpenReader(src)
        if err != nil {
            return err
        }
        defer func() {
            if err := r.Close(); err != nil {
                panic(err)
            }
        }()
    
        os.MkdirAll(dest, 0755)
    
        // Closure to address file descriptors issue with all the deferred .Close() methods
        extractAndWriteFile := func(f *zip.File) error {
            path := filepath.Join(dest, f.Name)
            // Check for ZipSlip: https://snyk.io/research/zip-slip-vulnerability
            if !strings.HasPrefix(path, dest) {
                return fmt.Errorf("%s: illegal file path", path)
            }
    
            rc, err := f.Open()
            if err != nil {
                return err
            }
            defer func() {
                if err := rc.Close(); err != nil {
                    panic(err)
                }
            }()
    
            if f.FileInfo().IsDir() {
                os.MkdirAll(path, f.Mode())
            } else {
                os.MkdirAll(filepath.Dir(path), f.Mode())
                f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
                if err != nil {
                    return err
                }
                defer func() {
                    if err := f.Close(); err != nil {
                        panic(err)
                    }
                }()
    
                _, err = io.Copy(f, rc)
                if err != nil {
                    return err
                }
            }
            return nil
        }
    
        for _, f := range r.File {
            err := extractAndWriteFile(f)
            if err != nil {
                return err
            }
        }
    
        return nil
    }
    
    0 讨论(0)
  • 2021-02-01 04:13

    there is Zip Slip Vulnerability in the code of @Astockwell , see: https://snyk.io/research/zip-slip-vulnerability

    0 讨论(0)
  • 2021-02-01 04:20

    I'm using archive/zip package to read .zip files and copy to the local disk. Below is the source code to unzip .zip files for my own needs.

    import (
        "archive/zip"
        "io"
        "log"
        "os"
        "path/filepath"
        "strings"
    )
    
    func unzip(src, dest string) error {
        r, err := zip.OpenReader(src)
        if err != nil {
            return err
        }
        defer r.Close()
    
        for _, f := range r.File {
            rc, err := f.Open()
            if err != nil {
                return err
            }
            defer rc.Close()
    
            fpath := filepath.Join(dest, f.Name)
            if f.FileInfo().IsDir() {
                os.MkdirAll(fpath, f.Mode())
            } else {
                var fdir string
                if lastIndex := strings.LastIndex(fpath,string(os.PathSeparator)); lastIndex > -1 {
                    fdir = fpath[:lastIndex]
                }
    
                err = os.MkdirAll(fdir, f.Mode())
                if err != nil {
                    log.Fatal(err)
                    return err
                }
                f, err := os.OpenFile(
                    fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
                if err != nil {
                    return err
                }
                defer f.Close()
    
                _, err = io.Copy(f, rc)
                if err != nil {
                    return err
                }
            }
        }
        return nil
    }
    
    0 讨论(0)
  • 2021-02-01 04:22

    Slight rework of the OP's solution to create the containing directory dest if it doesn't exist, and to wrap the file extraction/writing in a closure to eliminate stacking of defer .Close() calls per @Nick Craig-Wood's comment:

    func Unzip(src, dest string) error {
        r, err := zip.OpenReader(src)
        if err != nil {
            return err
        }
        defer func() {
            if err := r.Close(); err != nil {
                panic(err)
            }
        }()
    
        os.MkdirAll(dest, 0755)
    
        // Closure to address file descriptors issue with all the deferred .Close() methods
        extractAndWriteFile := func(f *zip.File) error {
            rc, err := f.Open()
            if err != nil {
                return err
            }
            defer func() {
                if err := rc.Close(); err != nil {
                    panic(err)
                }
            }()
    
            path := filepath.Join(dest, f.Name)
    
            // Check for ZipSlip (Directory traversal)
            if !strings.HasPrefix(path, filepath.Clean(dest) + string(os.PathSeparator)) {
                return fmt.Errorf("illegal file path: %s", path)
            }
    
            if f.FileInfo().IsDir() {
                os.MkdirAll(path, f.Mode())
            } else {
                os.MkdirAll(filepath.Dir(path), f.Mode())
                f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
                if err != nil {
                    return err
                }
                defer func() {
                    if err := f.Close(); err != nil {
                        panic(err)
                    }
                }()
    
                _, err = io.Copy(f, rc)
                if err != nil {
                    return err
                }
            }
            return nil
        }
    
        for _, f := range r.File {
            err := extractAndWriteFile(f)
            if err != nil {
                return err
            }
        }
    
        return nil
    }
    

    Note: Updated to include Close() error handling as well (if we're looking for best practices, may as well follow ALL of them).

    0 讨论(0)
提交回复
热议问题