mirror of
https://github.com/tickstep/aliyunpan.git
synced 2025-01-23 22:42:15 +08:00
278 lines
7.9 KiB
Go
278 lines
7.9 KiB
Go
// 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-api/aliyunpan"
|
|
"github.com/tickstep/aliyunpan-api/aliyunpan/apierror"
|
|
"github.com/tickstep/aliyunpan/cmder"
|
|
"github.com/tickstep/aliyunpan/internal/config"
|
|
"github.com/tickstep/library-go/logger"
|
|
"github.com/urfave/cli"
|
|
"io/ioutil"
|
|
"log"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type (
|
|
dirFileListData struct {
|
|
Dir *aliyunpan.MkdirResult
|
|
FileList aliyunpan.FileList
|
|
}
|
|
)
|
|
|
|
const (
|
|
DefaultSaveToPanPath = "/aliyunpan"
|
|
)
|
|
|
|
func CmdImport() cli.Command {
|
|
return cli.Command{
|
|
Name: "import",
|
|
Usage: "导入文件",
|
|
UsageText: cmder.App().Name + " export <本地元数据文件路径>",
|
|
Description: `
|
|
导入文件中记录的元数据文件到网盘。保存到网盘的文件会使用文件元数据记录的路径位置,如果没有指定云盘目录(saveto)则默认导入到目录 aliyunpan 中。
|
|
导入的文件可以使用 export 命令获得。
|
|
|
|
导入文件每一行是一个文件元数据,样例如下:
|
|
aliyunpan://file.dmg|752FCCBFB2436A6FFCA3B287831D4FAA5654B07E|7005440|pan_folder
|
|
|
|
示例:
|
|
导入文件 /Users/tickstep/Downloads/export_files.txt 存储的所有文件元数据项
|
|
aliyunpan import /Users/tickstep/Downloads/export_files.txt
|
|
|
|
导入文件 /Users/tickstep/Downloads/export_files.txt 存储的所有文件元数据项并保存到目录 /my2021 中
|
|
aliyunpan import -saveto=/my2021 /Users/tickstep/Downloads/export_files.txt
|
|
|
|
导入文件 /Users/tickstep/Downloads/export_files.txt 存储的所有文件元数据项并保存到网盘根目录 / 中
|
|
aliyunpan import -saveto=/ /Users/tickstep/Downloads/export_files.txt
|
|
`,
|
|
Category: "阿里云盘",
|
|
Before: ReloadConfigFunc,
|
|
Action: func(c *cli.Context) error {
|
|
if c.NArg() < 1 {
|
|
cli.ShowCommandHelp(c, c.Command.Name)
|
|
return nil
|
|
}
|
|
|
|
saveTo := ""
|
|
if c.String("saveto") != "" {
|
|
saveTo = filepath.Clean(c.String("saveto"))
|
|
}
|
|
|
|
subArgs := c.Args()
|
|
RunImportFiles(parseDriveId(c), c.Bool("ow"), saveTo, subArgs[0])
|
|
return nil
|
|
},
|
|
Flags: []cli.Flag{
|
|
cli.BoolFlag{
|
|
Name: "ow",
|
|
Usage: "overwrite, 覆盖已存在的网盘文件",
|
|
},
|
|
cli.StringFlag{
|
|
Name: "driveId",
|
|
Usage: "网盘ID",
|
|
Value: "",
|
|
},
|
|
cli.StringFlag{
|
|
Name: "saveto",
|
|
Usage: "将文件保存到指定的目录",
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func RunImportFiles(driveId string, overwrite bool, panSavePath, localFilePath string) {
|
|
lfi, _ := os.Stat(localFilePath)
|
|
if lfi != nil {
|
|
if lfi.IsDir() {
|
|
fmt.Println("请指定导入文件")
|
|
return
|
|
}
|
|
} else {
|
|
// create file
|
|
fmt.Println("导入文件不存在")
|
|
return
|
|
}
|
|
|
|
if panSavePath == "" {
|
|
// use default
|
|
panSavePath = DefaultSaveToPanPath
|
|
}
|
|
|
|
fmt.Println("导入的文件会存储到目录:" + panSavePath)
|
|
|
|
importFile, err := os.OpenFile(localFilePath, os.O_RDONLY, 0755)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
return
|
|
}
|
|
defer importFile.Close()
|
|
|
|
fileData, err := ioutil.ReadAll(importFile)
|
|
if err != nil {
|
|
fmt.Println("读取文件出错")
|
|
return
|
|
}
|
|
fileText := string(fileData)
|
|
if len(fileText) == 0 {
|
|
fmt.Println("文件为空")
|
|
return
|
|
}
|
|
fileText = strings.TrimSpace(fileText)
|
|
fileLines := strings.Split(fileText, "\n")
|
|
importFileItems := []RapidUploadItem{}
|
|
for _, line := range fileLines {
|
|
line = strings.TrimSpace(line)
|
|
if item, e := newRapidUploadItem(line); e != nil {
|
|
fmt.Println(e)
|
|
continue
|
|
} else {
|
|
item.FilePath = strings.ReplaceAll(path.Join(panSavePath, item.FilePath), "\\", "/")
|
|
importFileItems = append(importFileItems, *item)
|
|
}
|
|
}
|
|
if len(importFileItems) == 0 {
|
|
fmt.Println("没有可以导入的文件项目")
|
|
return
|
|
}
|
|
|
|
fmt.Println("正在准备导入...")
|
|
dirMap := prepareMkdir(driveId, importFileItems)
|
|
|
|
fmt.Println("正在导入...")
|
|
successImportFiles := []RapidUploadItem{}
|
|
failedImportFiles := []RapidUploadItem{}
|
|
for _, item := range importFileItems {
|
|
fmt.Printf("正在处理导入: %s\n", item.FilePath)
|
|
result, abort := processOneImport(driveId, overwrite, dirMap, item)
|
|
if abort {
|
|
fmt.Println("导入任务终止了")
|
|
break
|
|
}
|
|
if result {
|
|
successImportFiles = append(successImportFiles, item)
|
|
} else {
|
|
failedImportFiles = append(failedImportFiles, item)
|
|
}
|
|
time.Sleep(time.Duration(200) * time.Millisecond)
|
|
}
|
|
if len(failedImportFiles) > 0 {
|
|
fmt.Println("\n以下文件导入失败")
|
|
for _, f := range failedImportFiles {
|
|
fmt.Printf("%s %s\n", f.FileSha1, f.FilePath)
|
|
}
|
|
fmt.Println("")
|
|
}
|
|
fmt.Printf("导入结果, 成功 %d, 失败 %d\n", len(successImportFiles), len(failedImportFiles))
|
|
}
|
|
|
|
func processOneImport(driveId string, isOverwrite bool, dirMap map[string]*dirFileListData, item RapidUploadItem) (result, abort bool) {
|
|
panClient := config.Config.ActiveUser().PanClient()
|
|
panDir, fileName := path.Split(item.FilePath)
|
|
dataItem := dirMap[path.Dir(panDir)]
|
|
if isOverwrite {
|
|
// 标记覆盖旧同名文件
|
|
// 检查同名文件是否存在
|
|
var efi *aliyunpan.FileEntity = nil
|
|
for _, fileItem := range dataItem.FileList {
|
|
if !fileItem.IsFolder() && fileItem.FileName == fileName {
|
|
efi = fileItem
|
|
break
|
|
}
|
|
}
|
|
if efi != nil && efi.FileId != "" {
|
|
// existed, delete it
|
|
fdr, err := panClient.FileDelete([]*aliyunpan.FileBatchActionParam{
|
|
{
|
|
DriveId: driveId,
|
|
FileId: efi.FileId,
|
|
},
|
|
})
|
|
if err != nil || fdr == nil || !fdr[0].Success {
|
|
fmt.Println("无法删除文件,请稍后重试")
|
|
return false, false
|
|
}
|
|
time.Sleep(time.Duration(500) * time.Millisecond)
|
|
fmt.Println("检测到同名文件,已移动到回收站")
|
|
}
|
|
}
|
|
|
|
appCreateUploadFileParam := &aliyunpan.CreateFileUploadParam{
|
|
DriveId: driveId,
|
|
Name: fileName,
|
|
Size: item.FileSize,
|
|
ContentHash: item.FileSha1,
|
|
ParentFileId: dataItem.Dir.FileId,
|
|
}
|
|
uploadOpEntity, apierr := panClient.CreateUploadFile(appCreateUploadFileParam)
|
|
if apierr != nil {
|
|
fmt.Println("创建秒传任务失败:" + apierr.Error())
|
|
return false, true
|
|
}
|
|
|
|
if uploadOpEntity.RapidUpload {
|
|
logger.Verboseln("秒传成功, 保存到网盘路径: ", path.Join(panDir, uploadOpEntity.FileName))
|
|
} else {
|
|
fmt.Println("失败,文件未曾上传,无法秒传")
|
|
return false, false
|
|
}
|
|
return true, false
|
|
}
|
|
|
|
func prepareMkdir(driveId string, importFileItems []RapidUploadItem) map[string]*dirFileListData {
|
|
panClient := config.Config.ActiveUser().PanClient()
|
|
resultMap := map[string]*dirFileListData{}
|
|
for _, item := range importFileItems {
|
|
var apierr *apierror.ApiError
|
|
var rs *aliyunpan.MkdirResult
|
|
panDir := path.Dir(item.FilePath)
|
|
if resultMap[panDir] != nil {
|
|
continue
|
|
}
|
|
if panDir != "/" {
|
|
rs, apierr = panClient.MkdirRecursive(driveId, "", "", 0, strings.Split(path.Clean(panDir), "/"))
|
|
if apierr != nil || rs.FileId == "" {
|
|
logger.Verboseln("创建云盘文件夹失败")
|
|
continue
|
|
}
|
|
} else {
|
|
rs = &aliyunpan.MkdirResult{}
|
|
rs.FileId = aliyunpan.DefaultRootParentFileId
|
|
}
|
|
dataItem := &dirFileListData{}
|
|
dataItem.Dir = rs
|
|
|
|
// files
|
|
param := &aliyunpan.FileListParam{}
|
|
param.DriveId = driveId
|
|
param.ParentFileId = rs.FileId
|
|
allFileInfo, err1 := panClient.FileListGetAll(param, 0)
|
|
if err1 != nil {
|
|
logger.Verboseln("获取文件信息出错")
|
|
continue
|
|
}
|
|
dataItem.FileList = allFileInfo
|
|
|
|
resultMap[panDir] = dataItem
|
|
time.Sleep(time.Duration(500) * time.Millisecond)
|
|
}
|
|
return resultMap
|
|
}
|