add sync command

This commit is contained in:
xiaoyaofenfen 2022-06-09 19:58:16 +08:00
parent d0e7715f4d
commit f692e2d18b
6 changed files with 147 additions and 20 deletions

102
internal/command/sync.go Normal file
View File

@ -0,0 +1,102 @@
// Copyright (c) 2020 tickstep.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package command
import (
"fmt"
"github.com/tickstep/aliyunpan/cmder"
"github.com/tickstep/aliyunpan/internal/config"
"github.com/tickstep/aliyunpan/internal/syncdrive"
"github.com/tickstep/aliyunpan/internal/utils"
"github.com/tickstep/library-go/logger"
"github.com/urfave/cli"
"os"
"strings"
"time"
)
func CmdSync() cli.Command {
return cli.Command{
Name: "sync",
Usage: "同步备份功能(Beta)",
UsageText: cmder.App().Name + " sync",
Description: `
备份功能指定本地目录和对应的一个网盘目录以备份文件
备份功能支持一下三种模式
1. 备份本地文件即上传本地文件到网盘始终保持本地文件有一个完整的备份在网盘
2. 备份云盘文件即下载网盘文件到本地始终保持网盘的文件有一个完整的备份在本地
3. 双向备份保持网盘文件和本地文件严格一致
`,
Category: "阿里云盘",
Before: cmder.ReloadConfigFunc,
Action: func(c *cli.Context) error {
if config.Config.ActiveUser() == nil {
fmt.Println("未登录账号")
return nil
}
blockSize := int64(c.Int("bs") * 1024)
RunSync(blockSize)
return nil
},
Flags: []cli.Flag{
cli.BoolFlag{
Name: "log",
Usage: "开启log输出",
},
cli.IntFlag{
Name: "bs",
Usage: "block size上传分片大小单位KB。推荐值1024 ~ 10240",
Value: 10240,
},
},
}
}
func RunSync(uploadBlockSize int64) {
activeUser := GetActiveUser()
panClient := activeUser.PanClient()
// pan token expired checker
go func() {
for {
time.Sleep(time.Duration(1) * time.Minute)
if RefreshTokenInNeed(activeUser) {
logger.Verboseln("update access token for sync task")
panClient.UpdateToken(activeUser.WebToken)
}
}
}()
syncFolderRootPath := config.GetSyncDriveDir()
if b, e := utils.PathExists(syncFolderRootPath); e == nil {
if !b {
os.MkdirAll(syncFolderRootPath, 0600)
}
}
fmt.Println("启动同步备份进程")
syncMgr := syncdrive.NewSyncTaskManager(activeUser.DriveList.GetFileDriveId(), panClient, syncFolderRootPath)
if _, e := syncMgr.Start(); e != nil {
fmt.Println("启动任务失败:", e)
return
}
c := ""
for strings.ToLower(c) != "y" {
fmt.Print("本命令不会退出如需要结束同步备份进程请输入y然后按Enter键进行停止")
fmt.Scan(&c)
}
fmt.Println("正在停止同步备份任务,请稍等...")
syncMgr.Stop()
}

View File

@ -308,10 +308,16 @@ func GetConfigDir() string {
return configDir
}
// GetPluginDir 获取插件文件夹路径
func GetPluginDir() string {
return strings.TrimSuffix(GetConfigDir(), "/") + "/plugin"
}
// GetSyncDriveDir 获取同步备份的文件夹路径
func GetSyncDriveDir() string {
return strings.TrimSuffix(GetConfigDir(), "/") + "/sync_drive"
}
func (c *PanConfig) ActiveUser() *PanUser {
if c.activeUser == nil {
if c.UserList == nil {

View File

@ -235,10 +235,10 @@ func (f *FileActionTaskManager) doFileDiffRoutine(panFiles PanFileList, localFil
localFilesNeedToCheck, panFilesNeedToCheck := localFilesSet.Intersection(panFilesSet)
// download file from pan drive
if f.task.Mode == DownloadOnly || f.task.Mode == SyncTwoWay {
if panFilesNeedToDownload != nil {
for _, file := range panFilesNeedToDownload {
if file.ScanStatus == ScanStatusNormal { // 下载文件
if f.task.Mode == DownloadOnly || f.task.Mode == SyncTwoWay {
if file.IsFolder() {
if panFolderQueue != nil {
panFolderQueue.PushUnique(file)
@ -259,7 +259,9 @@ func (f *FileActionTaskManager) doFileDiffRoutine(panFiles PanFileList, localFil
},
}
f.addToSyncDb(fileActionTask)
}
} else if file.ScanStatus == ScanStatusDiscard { // 删除对应本地文件(文件夹)
if f.task.Mode == DownloadOnly || f.task.Mode == SyncTwoWay {
fileActionTask := &FileActionTask{
syncItem: &SyncFileItem{
Action: SyncFileActionDeleteLocal,
@ -274,16 +276,19 @@ func (f *FileActionTaskManager) doFileDiffRoutine(panFiles PanFileList, localFil
},
}
f.addToSyncDb(fileActionTask)
} else if f.task.Mode == UploadOnly {
// 删除无用记录
f.task.panFileDb.Delete(file.Path)
}
}
}
}
// upload file to pan drive
if f.task.Mode == UploadOnly || f.task.Mode == SyncTwoWay {
if localFilesNeedToUpload != nil {
for _, file := range localFilesNeedToUpload {
if file.ScanStatus == ScanStatusNormal { // 上传文件到云盘
if f.task.Mode == UploadOnly || f.task.Mode == SyncTwoWay {
if file.IsFolder() {
if localFolderQueue != nil {
localFolderQueue.PushUnique(file)
@ -304,7 +309,9 @@ func (f *FileActionTaskManager) doFileDiffRoutine(panFiles PanFileList, localFil
},
}
f.addToSyncDb(fileActionTask)
}
} else if file.ScanStatus == ScanStatusDiscard { // 删除对应云盘文件(文件夹)
if f.task.Mode == UploadOnly || f.task.Mode == SyncTwoWay {
fileActionTask := &FileActionTask{
syncItem: &SyncFileItem{
Action: SyncFileActionDeletePan,
@ -319,6 +326,9 @@ func (f *FileActionTaskManager) doFileDiffRoutine(panFiles PanFileList, localFil
},
}
f.addToSyncDb(fileActionTask)
} else if f.task.Mode == DownloadOnly {
// 删除无用记录
f.task.localFileDb.Delete(file.Path)
}
}
}
@ -355,6 +365,9 @@ func (f *FileActionTaskManager) doFileDiffRoutine(panFiles PanFileList, localFil
},
}
f.addToSyncDb(deletePanFile)
} else if f.task.Mode == DownloadOnly {
// 删除无用记录
f.task.localFileDb.Delete(localFile.Path)
}
continue
}
@ -375,6 +388,9 @@ func (f *FileActionTaskManager) doFileDiffRoutine(panFiles PanFileList, localFil
},
}
f.addToSyncDb(deletePanFile)
} else if f.task.Mode == UploadOnly {
// 删除无用记录
f.task.panFileDb.Delete(panFile.Path)
}
continue
}

View File

@ -25,7 +25,7 @@ type (
// Id 任务ID
Id string `json:"id"`
// DriveId 网盘ID目前支持文件网盘
DriveId string `json:"driveId"`
DriveId string `json:"-"`
// LocalFolderPath 本地目录
LocalFolderPath string `json:"localFolderPath"`
// PanFolderPath 云盘目录
@ -66,12 +66,12 @@ func (t *SyncTask) NameLabel() string {
func (t *SyncTask) String() string {
builder := &strings.Builder{}
builder.WriteString("任务: " + t.NameLabel() + "\n")
mode := "双向同步"
mode := "双向备份"
if t.Mode == UploadOnly {
mode = "只上传"
mode = "备份本地文件(只上传"
}
if t.Mode == UploadOnly {
mode = "只下载"
if t.Mode == DownloadOnly {
mode = "备份云盘文件(只下载"
}
builder.WriteString("同步模式: " + mode + "\n")
builder.WriteString("本地目录: " + t.LocalFolderPath + "\n")

View File

@ -8,6 +8,7 @@ import (
"github.com/tickstep/library-go/logger"
"io/ioutil"
"path"
"time"
)
type (
@ -63,9 +64,9 @@ func (m *SyncTaskManager) parseConfigFile() error {
m.syncDriveConfig = r
if b, _ := utils.PathExists(configFilePath); b != true {
text := utils.ObjectToJsonStr(r, true)
ioutil.WriteFile(configFilePath, []byte(text), 0600)
return nil
//text := utils.ObjectToJsonStr(r, true)
//ioutil.WriteFile(configFilePath, []byte(text), 0600)
return fmt.Errorf("备份配置文件不存在")
}
data, e := ioutil.ReadFile(configFilePath)
if e != nil {
@ -109,11 +110,10 @@ func (m *SyncTaskManager) Start() (bool, error) {
}
fmt.Println("\n启动同步任务")
fmt.Println(task)
time.Sleep(200 * time.Millisecond)
}
// save config file
ioutil.WriteFile(m.configFilePath(), []byte(utils.ObjectToJsonStr(m.syncDriveConfig, true)), 0600)
// TODO: refresh panClient token to keep access token alive
return true, nil
}

View File

@ -435,6 +435,9 @@ func main() {
// 备份 backup
command.CmdBackup(),
// 同步备份 sync
command.CmdSync(),
// 上传文件/目录 upload
command.CmdUpload(),