Rainbond/builder/sources/file.go

139 lines
3.5 KiB
Go

// 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 <http://www.gnu.org/licenses/>.
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
}