github.com/qiuhoude/go-web@v0.0.0-20220223060959-ab545e78f20d/prepare/08_file/demo/main.go (about) 1 package main 2 3 import ( 4 "archive/zip" 5 "bufio" 6 "crypto/md5" 7 "encoding/hex" 8 "flag" 9 "fmt" 10 "io" 11 "io/fs" 12 "log" 13 "os" 14 "path/filepath" 15 "regexp" 16 "strconv" 17 "strings" 18 "text/template" 19 "time" 20 ) 21 22 var ( 23 gameDirStr string 24 inManifestStr string 25 outManifestStr string 26 ) 27 28 func main() { 29 flag.StringVar(&gameDirStr, "gameDir", "", "游戏目录") 30 flag.StringVar(&inManifestStr, "inManifest", "version.manifest", "版本配置文件") 31 flag.StringVar(&outManifestStr, "outManifest", "version.manifest.new", "生成新版本配置文件") 32 flag.Parse() 33 34 // 压缩文件 35 err := os.Chdir(gameDirStr) // cd gameDir/ 36 if err != nil { 37 log.Fatal(err) 38 } 39 zipFile := gameDirStr + ".zip" 40 listFile := listDir("./", func(f fs.FileInfo) bool { 41 if f.IsDir() || 42 strings.HasSuffix(f.Name(), "cache.manifest") || 43 f.Name() == zipFile { 44 return false 45 } 46 return true 47 }) 48 49 compressZip(zipFile, listFile...) 50 51 // md5 和 size 52 md5Str, sz, err := hashFileMd5AndSize(zipFile) 53 if err != nil { 54 log.Fatal(err) 55 } 56 err = os.Chdir("../") 57 if err != nil { 58 log.Fatal(err) 59 } 60 log.Printf("计算md5值完成 %s -> md5:%s size:%v", zipFile, md5Str, sz) 61 62 // 读取 VersionManifest 63 vm, err := readVersionManifest(inManifestStr) 64 if err != nil { 65 log.Fatal(err) 66 } 67 68 // 新的stage数据 69 newStage := Stage{ 70 Name: zipFile, 71 Code: strings.ToUpper(md5Str), 72 Size: strconv.FormatInt(sz, 10), 73 } 74 75 // 计算每日版本号 76 now := time.Now() 77 curDateStr := now.Format("20060102") 78 todayVersion := 0 79 if len(vm.Stage) > 0 { 80 lastStage := vm.Stage[len(vm.Stage)-1] 81 n := len(lastStage.UpdateCode) - 1 82 dateStr := string(lastStage.UpdateCode[:n-2]) 83 vStr := string(lastStage.UpdateCode[n-2:]) 84 if curDateStr == dateStr { 85 i, err := strconv.Atoi(vStr) 86 if err != nil { 87 log.Fatal(err) 88 } 89 todayVersion = i 90 } 91 } 92 todayVersion++ 93 newStage.UpdateCode = fmt.Sprintf("%s%02d", curDateStr, todayVersion) 94 vm.UpdateCode = newStage.UpdateCode 95 vm.Stage = append(vm.Stage, newStage) // 添加到数据中 96 err = outPutVersionManifestFile(vm, outManifestStr) 97 if err != nil { 98 log.Fatal(err) 99 } 100 log.Println("成功完成...") 101 102 } 103 104 type fileFilter func(fileName fs.FileInfo) bool 105 106 type VersionManifest struct { 107 UpdateCode string 108 Stage []Stage 109 } 110 111 type Stage struct { 112 Name string 113 UpdateCode string 114 Code string 115 Size string 116 } 117 118 func listDir(dirName string, filter fileFilter) []string { 119 var rt []string 120 err := filepath.Walk(dirName, func(path string, info fs.FileInfo, err error) error { 121 if err != nil { 122 return err 123 } 124 if filter != nil && filter(info) { 125 rt = append(rt, path) 126 } 127 return nil 128 129 }) 130 if err != nil { 131 return nil 132 } 133 134 return rt 135 } 136 func rmFile(zipFileName string) { 137 if _, err := os.Stat(zipFileName); err == nil { 138 _ = os.Remove(zipFileName) 139 log.Printf("删除文件 %v", zipFileName) 140 } 141 } 142 143 // 压缩文件 144 func compressZip(zipFileName string, files ...string) { 145 //rmFile(zipFileName) 146 file, err := os.OpenFile(zipFileName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) 147 //file, err := os.Create(zipFileName) 148 if err != nil { 149 log.Fatalf("Failed to open zip for writing: %s", err) 150 } 151 defer file.Close() 152 153 zipw := zip.NewWriter(file) 154 defer zipw.Close() 155 totalSize := len(files) 156 for i, filename := range files { 157 if err := appendFiles(filename, zipw); err != nil { 158 log.Fatalf("Failed to add file %s to zip: %s", filename, err) 159 } 160 fmt.Printf("\r>>>progress 正在压缩文件 (%d/%d) ", i+1, totalSize) 161 } 162 fmt.Println() 163 fmt.Printf("压缩文件完成 %s \n", zipFileName) 164 165 } 166 167 func appendFiles(filename string, zipw *zip.Writer) error { 168 file, err := os.Open(filename) 169 if err != nil { 170 return fmt.Errorf("Failed to open %s: %s", filename, err) 171 } 172 defer file.Close() 173 174 wr, err := zipw.Create(filename) 175 if err != nil { 176 msg := "Failed to create entry for %s in zip file: %s" 177 return fmt.Errorf(msg, filename, err) 178 } 179 180 if _, err := io.Copy(wr, file); err != nil { 181 return fmt.Errorf("Failed to write %s to zip: %s", filename, err) 182 } 183 return nil 184 } 185 186 func hashFileMd5AndSize(filePath string) (md5Str string, filesz int64, err error) { 187 file, err := os.Open(filePath) 188 if err != nil { 189 return 190 } 191 defer file.Close() 192 hash := md5.New() 193 if _, err = io.Copy(hash, file); err != nil { 194 return 195 } 196 info, err := file.Stat() 197 if err != nil { 198 return 199 } 200 hashInBytes := hash.Sum(nil)[:16] 201 md5Str = hex.EncodeToString(hashInBytes) 202 filesz = info.Size() 203 return 204 } 205 206 func readFileLine(path string, doFunc func(line string)) error { 207 file, err := os.Open(path) 208 if err != nil { 209 return err 210 } 211 defer file.Close() 212 scanner := bufio.NewScanner(file) 213 for scanner.Scan() { 214 line := scanner.Text() 215 doFunc(line) 216 } 217 if err := scanner.Err(); err != nil { 218 return err 219 } 220 return nil 221 } 222 223 func readVersionManifest(path string) (v VersionManifest, err error) { 224 // (\w+)\s*=\s*"([\w|_|\.|\d|]+)" 225 //codeRe := regexp.MustCompile(`updateCode = "([\w|_|\.|\d|]+)"`) // 外层的updateCode 226 stageRe := regexp.MustCompile(`(\w+)="([\w|_|\.|\d|]+)"`) 227 v = VersionManifest{} 228 err = readFileLine(path, func(line string) { 229 arr := stageRe.FindAllStringSubmatch(line, -1) 230 if len(arr) > 0 { 231 var s = Stage{} 232 for _, d := range arr { 233 switch d[1] { 234 case "name": 235 s.Name = d[2] 236 case "updateCode": 237 s.UpdateCode = d[2] 238 case "code": 239 s.Code = d[2] 240 case "size": 241 s.Size = d[2] 242 } 243 } 244 v.Stage = append(v.Stage, s) 245 } 246 }) 247 if err != nil { 248 return 249 } 250 return 251 } 252 253 func outPutVersionManifestFile(v VersionManifest, fp string) error { 254 file, err := os.OpenFile(fp, os.O_CREATE|os.O_WRONLY, os.ModePerm) 255 if err != nil { 256 return err 257 } 258 if err != nil { 259 return err 260 } 261 luaTemplate := template.Must(template.New("").Parse(`local m={ 262 updateCode = "{{.UpdateCode}}", 263 stage={ 264 {{- range .Stage }} 265 {{"\t\t"}}{name="{{.Name}}",updateCode="{{.UpdateCode}}",code="{{.Code}}",size="{{.Size}}"}, 266 {{- end }} 267 } 268 } 269 return m`)) 270 err = luaTemplate.Execute(file, v) 271 return err 272 }