// Copyright (C) 2014-2018 Goodrain Co., Ltd. // RAINBOND, Application Management Platform // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. For any non-GPL usage of Rainbond, // one or multiple Commercial Licenses authorized by Goodrain Co., Ltd. // must be obtained first. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see . package sources import ( "fmt" "io" "os" "path/filepath" "github.com/Sirupsen/logrus" "github.com/twinj/uuid" "github.com/goodrain/rainbond/util" "github.com/goodrain/rainbond/event" ) //CopyFileWithProgress 复制文件,带进度 func CopyFileWithProgress(src, dst string, logger event.Logger) error { srcFile, err := os.OpenFile(src, os.O_RDONLY, 0644) if err != nil { if logger != nil { logger.Error("打开源文件失败", map[string]string{"step": "share"}) } logrus.Errorf("open file %s error", src) return err } defer srcFile.Close() srcStat, err := srcFile.Stat() if err != nil { if logger != nil { logger.Error("打开源文件失败", map[string]string{"step": "share"}) } return err } // 验证并创建目标目录 dir := filepath.Dir(dst) if err := util.CheckAndCreateDir(dir); err != nil { if logger != nil { logger.Error("检测并创建目标文件目录失败", map[string]string{"step": "share"}) } return err } // 先删除文件如果存在 os.RemoveAll(dst) dstFile, err := os.OpenFile(dst, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0644) if err != nil { if logger != nil { logger.Error("打开目标文件失败", map[string]string{"step": "share"}) } return err } defer dstFile.Close() allSize := srcStat.Size() return CopyWithProgress(srcFile, dstFile, allSize, logger) } //SrcFile 源文件 type SrcFile interface { Read([]byte) (int, error) } //DstFile 目标文件 type DstFile interface { Write([]byte) (int, error) } //CopyWithProgress copy file func CopyWithProgress(srcFile SrcFile, dstFile DstFile, allSize int64, logger event.Logger) (err error) { var written int64 buf := make([]byte, 1024*1024) progressID := uuid.NewV4().String()[0:7] for { nr, er := srcFile.Read(buf) if nr > 0 { nw, ew := dstFile.Write(buf[0:nr]) if nw > 0 { written += int64(nw) } if ew != nil { err = ew break } if nr != nw { err = io.ErrShortWrite break } } if er != nil { if er != io.EOF { err = er } break } if logger != nil { progress := "[" i := int((float64(written) / float64(allSize)) * 50) if i == 0 { i = 1 } for j := 0; j < i; j++ { progress += "=" } progress += ">" for len(progress) < 50 { progress += " " } progress += fmt.Sprintf("] %d MB/%d MB", int(written/1024/1024), int(allSize/1024/1024)) message := fmt.Sprintf(`{"progress":"%s","progressDetail":{"current":%d,"total":%d},"id":"%s"}`, progress, written, allSize, progressID) logger.Debug(message, map[string]string{"step": "progress"}) } } if err != nil { return err } if written != allSize { return io.ErrShortWrite } return nil }