|
5 | 5 | "io" |
6 | 6 | "os" |
7 | 7 | "path/filepath" |
| 8 | + "strings" |
8 | 9 | ) |
9 | 10 |
|
10 | 11 | // saveToFile writes the reader to the given file path, creating directories as needed. |
@@ -33,8 +34,45 @@ func unzip(zipPath, destDir string) error { |
33 | 34 | return err |
34 | 35 | } |
35 | 36 |
|
| 37 | + // Detect if the zip contains a single top-level directory that matches |
| 38 | + // the destination base directory name. If so, strip that component to |
| 39 | + // avoid nested same-name directories like destDir/destDir/... |
| 40 | + base := filepath.Base(destDir) |
| 41 | + topLevels := make(map[string]struct{}) |
36 | 42 | for _, f := range zr.File { |
37 | | - p := filepath.Join(destDir, f.Name) |
| 43 | + // Normalize separators in zip entries |
| 44 | + name := strings.TrimLeft(strings.ReplaceAll(f.Name, "\\", "/"), "/") |
| 45 | + if name == "" { |
| 46 | + continue |
| 47 | + } |
| 48 | + // Extract first path component |
| 49 | + if i := strings.IndexByte(name, '/'); i >= 0 { |
| 50 | + topLevels[name[:i]] = struct{}{} |
| 51 | + } else { |
| 52 | + topLevels[name] = struct{}{} |
| 53 | + } |
| 54 | + } |
| 55 | + |
| 56 | + var stripPrefix string |
| 57 | + if len(topLevels) == 1 { |
| 58 | + for tl := range topLevels { |
| 59 | + if tl == base { |
| 60 | + stripPrefix = tl + "/" |
| 61 | + } |
| 62 | + } |
| 63 | + } |
| 64 | + |
| 65 | + for _, f := range zr.File { |
| 66 | + // Normalize and optionally strip the top-level prefix |
| 67 | + name := strings.TrimLeft(strings.ReplaceAll(f.Name, "\\", "/"), "/") |
| 68 | + if stripPrefix != "" { |
| 69 | + name = strings.TrimPrefix(name, stripPrefix) |
| 70 | + } |
| 71 | + if name == "" { |
| 72 | + // nothing to create (e.g., top-level dir entry when stripped) |
| 73 | + continue |
| 74 | + } |
| 75 | + p := filepath.Join(destDir, name) |
38 | 76 | if f.FileInfo().IsDir() { |
39 | 77 | if err := os.MkdirAll(p, f.Mode()); err != nil { |
40 | 78 | return err |
@@ -63,4 +101,3 @@ func unzip(zipPath, destDir string) error { |
63 | 101 | } |
64 | 102 | return nil |
65 | 103 | } |
66 | | - |
|
0 commit comments