package service import ( "basic.com/valib/logger.git" "bufio" "errors" "fmt" "io/ioutil" "mime/multipart" "os" "os/exec" "path" "strings" "webserver/extend/config" "webserver/extend/util" ) type SysService struct { } type FileChunkCheckVo struct { UserId string ChunkNumber int //当前分片下标,从1开始 ChunkSize int //每一块的大小 CurrentChunkSize int //当前分块的大小 FileName string //文件名称 Identifier string //整个文件唯一标识,md5 RelativePath string //文件客户端路径 TotalSize int64 //文件总大小 TotalChunks int //总分片数量 } type FileUploadVo struct { Id string UserId string ChunkNumber int //当前分片下标,从1开始 ChunkSize int //每一块的大小 CurrentChunkSize int //当前分块的大小 FileName string //文件名称 Identifier string //整个文件唯一标识,md5 RelativePath string //文件客户端路径 TotalSize int64 //文件总大小 TotalChunks int //总分片数量 File *multipart.File //当前分片的文件内容 Header *multipart.FileHeader } func (sv SysService) CheckUpdateFile(arg *FileChunkCheckVo) bool { configPatchPath := "" if config.Server.PatchPath != "" { configPatchPath = config.Server.PatchPath } else { configPatchPath = "/opt/vasystem/patch" } fileTmpPath := configPatchPath + "/"+arg.Identifier if !util.Exists(fileTmpPath) { return false } //判断合成的文件是否存在 index := strings.LastIndex(arg.FileName, ".") subfix := "" if index >-1 {//有后缀 subfix = arg.FileName[index:] } mergedFilePath := fileTmpPath+subfix if util.Exists(mergedFilePath) { return true } //判断分块文件是否存在 chunkAlignNum := util.FormatNum(arg.TotalChunks, arg.ChunkNumber) chunkFilePath := fileTmpPath+"/"+arg.Identifier+"_"+chunkAlignNum if !util.Exists(chunkFilePath) { return false } if arg.ChunkNumber == arg.TotalChunks { dirFiles, _ := ioutil.ReadDir(fileTmpPath) if dirFiles != nil && len(dirFiles) == arg.TotalChunks { //表示所有分块都上传了,需要merge if !MergeChunks(fileTmpPath, mergedFilePath) { return false } } } return true } func (sv SysService) PatchUpload(arg *FileUploadVo) (bool,bool) { configPatchPath := "" if config.Server.PatchPath != "" { configPatchPath = config.Server.PatchPath } else { configPatchPath = "/opt/vasystem/patch" } defer (*arg.File).Close() if !util.CreateDirectory(configPatchPath) { return false, false } filenameWithSuffix := path.Base(arg.Header.Filename) subfix := path.Ext(filenameWithSuffix) MD5Str := arg.Identifier logger.Debug("Identifier:",MD5Str) fileTmpPath := configPatchPath + "/"+MD5Str if !util.Exists(fileTmpPath) { if !util.CreateDirectory(fileTmpPath) { return false, false } } chunkAlignNum := util.FormatNum(arg.TotalChunks, arg.ChunkNumber) fileSavePath := fileTmpPath+"/"+MD5Str+"_"+chunkAlignNum if util.Exists(fileSavePath) { rmErr := os.Remove(fileSavePath) if rmErr != nil { logger.Debug("rmErr:",rmErr) return false, false } } file, e := os.Create(fileSavePath) if e !=nil { logger.Debug("os.Create err:",e,"fileSavePath:",fileSavePath) return false, false } defer file.Close() writer := bufio.NewWriter(file) chunkData := make([]byte, arg.Header.Size) n, err := (*arg.File).ReadAt(chunkData, 0) if n ==0 || err !=nil { logger.Debug("read chunkData err:",err,"n:",n) return false, false } nn, err2 := writer.Write(chunkData) if nn ==0 || err2 !=nil { logger.Debug("write chunkData err:",err2,"nn:",nn) return false, false } if err = writer.Flush(); err != nil { logger.Debug("write flush err:",err) } isComplete := false dirFiles, _ := ioutil.ReadDir(fileTmpPath) if dirFiles != nil && len(dirFiles) == arg.TotalChunks { isComplete = true } if isComplete { if MergeChunks(fileTmpPath,fileTmpPath + subfix) { logger.Debug("merge all chunks success,identifier:",MD5Str,"fileName:",arg.FileName) } else { return false, isComplete } } return true, isComplete } //upgrade func (sv SysService) Upgrade(identifier string,filename string) (bool,error) { if !bakBeforeUpgrade() { return false,errors.New("更新前备份失败") } configPatchPath := "" if config.Server.PatchPath != "" { configPatchPath = config.Server.PatchPath } else { configPatchPath = "/opt/vasystem/patch" } filenameWithSuffix := path.Base(filename) ext := path.Ext(filenameWithSuffix) zipFilePath := configPatchPath + "/"+identifier+ext if util.Exists(zipFilePath) { //校验md5 strMd5, e := util.FileMd5(zipFilePath) if e !=nil || strMd5 == "" { return false,errors.New("获取升级压缩包md5失败") } if strMd5 == identifier { if !updatePatch(identifier, ext) { return false,errors.New("执行升级过程异常,请确定上传的补丁是tar.gz格式") } return true,nil } else { logger.Debug("strMd5 is", strMd5,"identifier is",identifier,"not equal") return false,errors.New("校验升级文件失败") } } else { return false,errors.New("升级文件已丢失,请重新上传") } } func bakBeforeUpgrade() bool { configBakPath := "" if config.Server.BakPath != "" { configBakPath = config.Server.BakPath } else { configBakPath = "/opt/vasystem/bak" } if util.Exists(configBakPath) { //只保留最新的版本 if err := os.RemoveAll(configBakPath);err != nil { return false } } if !util.CreateDirectory(configBakPath) { return false } b, err := ExecCmd("cp -r /opt/vasystem/bin /opt/vasystem/bak") if err != nil { logger.Debug("bakBeforeUpgrade result:",string(b),"err:",err) return false } return true } //更新系统程序 func updatePatch(identifier string, ext string) bool { configPatchPath := "" if config.Server.PatchPath != "" { configPatchPath = config.Server.PatchPath } else { configPatchPath = "/opt/vasystem/patch" } //1.解压缩更新包 unPackPath := configPatchPath+"/"+identifier+"_basic/" if util.Exists(unPackPath) { //此版本已经更新过 rmErr := os.RemoveAll(unPackPath) if rmErr !=nil { return false } } if !util.CreateDirectory(unPackPath) { return false } unPackFilePath := configPatchPath+"/"+identifier+ext err := util.UnTarGz(unPackFilePath, unPackPath) if err !=nil { logger.Debug("UnPack err:",err,"unPackFile:",unPackFilePath) return false } //如果通用脚本有更新,则更新通用脚本 if util.Exists(unPackPath+"updatePatch.sh") { cpStr := fmt.Sprintf("cp %s /opt/vasystem/bin",unPackPath+"updatePatch.sh") b, err := ExecCmd(cpStr) if err != nil { logger.Debug("cp updatePatch.sh to bin err:",err,"result:",string(b)) return false } } //判断更新包里是否有补丁脚本,如果有则执行,否则执行updatePatch.sh updateCmd := fmt.Sprintf("./updatePatch.sh %s %s %s &",unPackPath,unPackFilePath,configPatchPath+"/"+identifier) if util.Exists(unPackPath+"upgrade.sh") { updateCmd = fmt.Sprintf("%supgrade.sh %s %s %s &",unPackPath,unPackPath,unPackFilePath,configPatchPath+"/"+identifier) } //2.更新系统 b,err := ExecCmd(updateCmd) if err != nil { logger.Debug("upgrade err:",err,"result:",string(b),"cmd:",updateCmd) return false } else { logger.Debug("upgrade result:",string(b),"cmd:",updateCmd) } return true } func MergeChunks(chunkPath string, storePath string) bool { var cmd *exec.Cmd cmd = exec.Command("/bin/sh", "-c", fmt.Sprintf("./mergeAll.sh %s %s", chunkPath, storePath)) if b, err := cmd.Output(); err != nil { logger.Debug("mergeChunks err:", err, "result:", string(b)) return false } else { logger.Debug("mergeChunks result:",string(b),"cmd: ./mergeAll.sh ", chunkPath, storePath) return true } } func ExecCmd(cmdStr string) ([]byte,error) { var cmd *exec.Cmd cmd = exec.Command("/bin/sh", "-c",cmdStr) return cmd.Output() }