package main import ( "archive/zip" "bytes" "fmt" "io" "io/ioutil" "net/http" "os" "path/filepath" "runtime" "strconv" "strings" dkignore "github.com/codeskyblue/dockerignore" ) type Zip struct { *zip.Writer } func sanitizedName(filename string) string { if len(filename) > 1 && filename[1] == ':' && runtime.GOOS == "windows" { filename = filename[2:] } filename = strings.TrimLeft(strings.Replace(filename, `\`, "/", -1), `/`) filename = filepath.ToSlash(filename) filename = filepath.Clean(filename) return filename } func statFile(filename string) (info os.FileInfo, reader io.ReadCloser, err error) { info, err = os.Lstat(filename) if err != nil { return } // content if info.Mode()&os.ModeSymlink != 0 { var target string target, err = os.Readlink(filename) if err != nil { return } reader = ioutil.NopCloser(bytes.NewBuffer([]byte(target))) } else if !info.IsDir() { reader, err = os.Open(filename) if err != nil { return } } else { reader = ioutil.NopCloser(bytes.NewBuffer(nil)) } return } func (z *Zip) Add(relpath, abspath string) error { info, rdc, err := statFile(abspath) if err != nil { return err } defer rdc.Close() hdr, err := zip.FileInfoHeader(info) if err != nil { return err } hdr.Name = sanitizedName(relpath) if info.IsDir() { hdr.Name += "/" } hdr.Method = zip.Deflate // compress method writer, err := z.CreateHeader(hdr) if err != nil { return err } _, err = io.Copy(writer, rdc) return err } func CompressToZip(w http.ResponseWriter, rootDir string) { rootDir = filepath.Clean(rootDir) zipFileName := filepath.Base(rootDir) + ".zip" w.Header().Set("Content-Type", "application/zip") w.Header().Set("Content-Disposition", `attachment; filename="`+zipFileName+`"`) zw := &Zip{Writer: zip.NewWriter(w)} defer zw.Close() filepath.Walk(rootDir, func(path string, info os.FileInfo, err error) error { zipPath := path[len(rootDir):] if info.Name() == YAMLCONF { // ignore .ghs.yml for security return nil } return zw.Add(zipPath, path) }) } func ExtractFromZip(zipFile, path string, w io.Writer) (err error) { cf, err := zip.OpenReader(zipFile) if err != nil { return } defer cf.Close() rd := ioutil.NopCloser(bytes.NewBufferString(path)) patterns, err := dkignore.ReadIgnore(rd) if err != nil { return } for _, file := range cf.File { matched, _ := dkignore.Matches(file.Name, patterns) if !matched { continue } rc, er := file.Open() if er != nil { err = er return } defer rc.Close() _, err = io.Copy(w, rc) if err != nil { return } return } return fmt.Errorf("File %s not found", strconv.Quote(path)) }