Skip to content

Commit 59efd78

Browse files
committed
支持上传到OneDrive任意文件夹
1 parent 6d48306 commit 59efd78

File tree

6 files changed

+104
-39
lines changed

6 files changed

+104
-39
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ dist
77
info
88
*.json
99
*.exe
10-
od
10+
od
11+
TestUploadFolder

Readme.md

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55

66
- 支持 国际版, 个人版(家庭版), ~~中国版(世纪互联)~~.
7-
- ~~支持上传文件和文件夹到指定目录,~~ 保持上传前的目录结构.
7+
- 支持上传文件和文件夹到指定目录,并保持上传前的目录结构.
88
- 支持命令参数使用, 方便外部程序调用.
99
- 支持自定义上传分块大小.
1010
- 支持多线程上传(多文件同时上传).
@@ -53,7 +53,6 @@ Usage of OneDriveUploader:
5353
// 上传单个文件时,在网盘中重命名
5454
Rename file on upload to remote.
5555
56-
//此参数未设计,暂不可用
5756
-r string
5857
// 上传到网盘中的某个目录, 默认: 根目录
5958
Upload to reomte path.
@@ -112,30 +111,27 @@ Usage of OneDriveUploader:
112111
OneDriveUploader -c xxx.json -f "mm00.jpg"
113112

114113
# 将同目录下的 mm00.jpg 文件上传到 OneDrive 网盘根目录,并改名为 mm01.jpg(暂不可用)
115-
OneDriveUploader -c xxx.json -s "mm00.jpg" -n "mm01.jpg"
114+
OneDriveUploader -c xxx.json -f "mm00.jpg" -n "mm01.jpg"
116115

117116
# 将同目录下的 Download 文件夹上传到 OneDrive 网盘根目录
118-
OneDriveUploader -c xxx.json -s "Download"
117+
OneDriveUploader -c xxx.json -f "Download"
119118

120-
# 将同目录下的 Download 文件夹上传到 OneDrive 网盘Test目录中(暂不可用)
121-
OneDriveUploader -c xxx.json -s "Download" -r "Test"
119+
# 将同目录下的 Download 文件夹上传到 OneDrive 网盘Test目录中
120+
OneDriveUploader -c xxx.json -f "Download" -r "Test"
122121

123122
# 将同目录下的 Download 文件夹上传到 OneDrive 网盘根目录中, 使用 10 线程
124-
OneDriveUploader -c xxx.json -t 10 -s "Download"
123+
OneDriveUploader -c xxx.json -t 10 -f "Download"
125124

126125
# 将同目录下的 Download 文件夹上传到 OneDrive 网盘根目录中, 使用 10 线程,同时使用 Telegram Bot 实时监控上传进度
127-
OneDriveUploader -c xxx.json -t 10 -s "Download" -tgbot "123456:xxxxxxxx" -uid 123456789
126+
OneDriveUploader -c xxx.json -t 10 -f "Download" -tgbot "123456:xxxxxxxx" -uid 123456789
128127

129128
# 将同目录下的 Download 文件夹上传到 OneDrive 网盘根目录中, 使用 15 线程, 并设置分块大小为 20M
130-
OneDriveUploader -c xxx.json -t 15 -b 20 -s "Download"
129+
OneDriveUploader -c xxx.json -t 15 -b 20 -f "Download"
131130

132131
# 将同目录下的 Download 文件夹上传到 OneDrive 网盘Test目录中, 使用配置文件中的线程参数和分块大小参数(暂不可用)
133-
OneDriveUploader -f -c "/urs/local/auth.json" -s "Download" -r "Test"
132+
OneDriveUploader -c "/urs/local/auth.json" -f "Download" -r "Test" -force
134133

135134
# 将同目录下的 Download 文件夹上传到 OneDrive 网盘Test目录中, 使用配置文件中的线程参数和分块大小参数,并跳过上传网盘中已存在的同名文件(暂不可用)
136-
OneDriveUploader -f -c "/urs/local/auth.json" -skip -s "Download" -r "Test"
135+
OneDriveUploader -c "/urs/local/auth.json" -skip -f "Download" -r "Test"
137136
```
138137

139-
## 注意
140-
- ~~多次尝试后, 无失败的上传文件. 退出码为 0 .~~
141-
- ~~最终还有失败的上传文件会详细列出上传失败项. 退出码为 1.~~

api/restore/upload/onedriveRecoverableRestore.go

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"log"
77
"main/fileutil"
88
"net/http"
9+
"path/filepath"
910
"runtime/debug"
1011
"strconv"
1112
"time"
@@ -16,9 +17,9 @@ const (
1617
uploadURLKey = "uploadUrl"
1718
)
1819

19-
func (rs *RestoreService) recoverableUpload(userID string, bearerToken string, conflictOption string, filePath string, fileInfo fileutil.FileInfo, sendMsg func(text string), locText func(text string) string, username string) []map[string]interface{} {
20+
func (rs *RestoreService) recoverableUpload(userID string, bearerToken string, conflictOption string, targetFolder string, filePath string, fileInfo fileutil.FileInfo, sendMsg func(text string), locText func(text string) string, username string) []map[string]interface{} {
2021
//1. Get recoverable upload session for the current file path 获取当前文件路径的可压缩上载会话
21-
uploadSessionData, err := rs.getUploadSession(userID, bearerToken, conflictOption, filePath)
22+
uploadSessionData, err := rs.getUploadSession(userID, bearerToken, conflictOption, targetFolder, filePath)
2223
if err != nil {
2324
log.Panicf("Failed to Restore :%v", err)
2425
}
@@ -31,6 +32,12 @@ func (rs *RestoreService) recoverableUpload(userID string, bearerToken string, c
3132
if err != nil {
3233
log.Panicf("Failed to Restore :%v", err)
3334
}
35+
_size, err := fileutil.GetFileSize(filePath)
36+
if err != nil {
37+
log.Panicf("Failed to Restore :%v", err)
38+
}
39+
//log.Panicln(fileutil.Byte2Readable(size))
40+
size := byte2Readable(float64(_size))
3441

3542
//4. Loop over the file start offset list to read files in chunk and upload in onedrive 在文件开始偏移量列表上循环以读取块中的文件并在onedrive中上载
3643
var uploadResp []map[string]interface{}
@@ -39,6 +46,7 @@ func (rs *RestoreService) recoverableUpload(userID string, bearerToken string, c
3946
timeUnix := time.Now().UnixNano()
4047
var buffer = make([]byte, fileutil.GetDefaultChunkSize())
4148
startTime := time.Now().Unix()
49+
4250
for i, sOffset := range startOffsetLst {
4351
if i == lastChunkIndex {
4452
lastChunkSize, err := fileutil.GetLatsChunkSizeInBytes(filePath)
@@ -55,9 +63,9 @@ func (rs *RestoreService) recoverableUpload(userID string, bearerToken string, c
5563
log.Panicf("Failed to Restore :%v", err)
5664
}
5765
if i != 0 {
58-
sendMsg(fmt.Sprintf("正在向OneDrive账户 `%s` 上传 `%s` *『%d/%d』* \n速度:`%s/s` \n已耗时: `%d s`", username, filePath, i, len(startOffsetLst), byte2Readable(float64(fileutil.GetDefaultChunkSize())/float64(time.Now().UnixNano()-timeUnix)*float64(1000000000)), time.Now().Unix()-startTime))
66+
sendMsg(fmt.Sprintf("正在向OneDrive账户 `%s` 上传 `%s`\n 大小:`%s` 已上传大小: `%s` 进度: *『%d/%d』* \n速度:`%s/s` \n已耗时: `%d s`", username, filePath, size, byte2Readable(float64(fileutil.GetDefaultChunkSize())*float64(i)), i, len(startOffsetLst), byte2Readable(float64(fileutil.GetDefaultChunkSize())/float64(time.Now().UnixNano()-timeUnix)*float64(1000000000)), time.Now().Unix()-startTime))
5967
} else {
60-
sendMsg(fmt.Sprintf("正在向OneDrive账户 `%s` 上传 `%s` *『%d/%d』* \n速度:`----` \n已耗时: `%d s`", username, filePath, i, len(startOffsetLst), time.Now().Unix()-startTime))
68+
sendMsg(fmt.Sprintf("正在向OneDrive账户 `%s` 上传 `%s`\n 大小:`%s` 已上传大小: `%s` 进度: *『%d/%d』* \n速度:`----` \n已耗时: `%d s`", username, filePath, size, "0", i, len(startOffsetLst), time.Now().Unix()-startTime))
6169
}
6270

6371
timeUnix = time.Now().UnixNano()
@@ -92,8 +100,9 @@ func (rs *RestoreService) recoverableUpload(userID string, bearerToken string, c
92100
}
93101

94102
//Returns the restore session url for part file upload
95-
func (rs *RestoreService) getUploadSession(userID string, bearerToken string, conflictOption string, filePath string) (map[string]interface{}, error) {
96-
uploadSessionPath := fmt.Sprintf(uploadSessionPath, userID, filePath)
103+
func (rs *RestoreService) getUploadSession(userID string, bearerToken string, conflictOption string, targetFolder string, filePath string) (map[string]interface{}, error) {
104+
targetPath := filepath.Join(targetFolder, filePath)
105+
uploadSessionPath := fmt.Sprintf(uploadSessionPath, userID, targetPath)
97106
uploadSessionData := make(map[string]interface{})
98107
//Get the body for resemble upload session call.
99108
body, err := getRessumableSessionBody(filePath, conflictOption)

api/restore/upload/onedriveRestore.go

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
httpLocal "main/graph/net/http"
1010
"net/http"
1111
"net/url"
12+
"path/filepath"
1213
)
1314

1415
const (
@@ -35,16 +36,17 @@ type RestoreService struct {
3536
//@bearerToken will be extracted as sent from the restore input xml
3637
//@filePath will be extracted from the file hierarchy the needs to be restored
3738
//@fileInfo it is the file info struct that contains the actual file reference and the size_type
38-
func (rs *RestoreService) SimpleUploadToOriginalLoc(userId string, bearerToken string, conflictOption string, filePath string, fileInfo fileutil.FileInfo, sendMsg func(text string), locText func(text string) string, username string) interface{} {
39+
func (rs *RestoreService) SimpleUploadToOriginalLoc(userId string, bearerToken string, conflictOption string, targetFolder string, filePath string, fileInfo fileutil.FileInfo, sendMsg func(text string), locText func(text string) string, username string) interface{} {
3940
if fileInfo.SizeType == fileutil.SizeTypeLarge {
4041
//For Large file type use resemble onedrive upload API
4142
//log.Printf("Processing Large File: %s", filePath)
4243
sendMsg(fmt.Sprintf("文件: `%s` 开始上传至OneDrive\n账户:`%s`\n文件超过4MB,进入大文件通道", filePath, username))
43-
return rs.recoverableUpload(userId, bearerToken, conflictOption, filePath, fileInfo, sendMsg, locText, username)
44+
return rs.recoverableUpload(userId, bearerToken, conflictOption, targetFolder, filePath, fileInfo, sendMsg, locText, username)
4445
} else {
4546
//log.Printf("Processing Small File: %s", filePath)
4647
sendMsg(fmt.Sprintf("文件: `%s` 开始上传至OneDrive\n账户:`%s`\n文件小于4MB,进入小文件通道,上传中", filePath, username))
47-
uploadPath := fmt.Sprintf(simpleUploadPath, userId, filePath)
48+
targetPath := filepath.Join(targetFolder, filePath)
49+
uploadPath := fmt.Sprintf(simpleUploadPath, userId, targetPath)
4850
req, err := rs.NewRequest("PUT", uploadPath, getSimpleUploadHeader(bearerToken), fileInfo.FileData)
4951
if err != nil {
5052
log.Panicf("Failed to Restore :%v", err)
@@ -56,7 +58,16 @@ func (rs *RestoreService) SimpleUploadToOriginalLoc(userId string, bearerToken s
5658
req.URL.RawQuery = q.Encode()
5759

5860
//Execute the request
59-
resp, err := rs.Do(req)
61+
62+
var resp *http.Response
63+
for errCount := 1; errCount < 10; errCount++ {
64+
resp, err = rs.Do(req)
65+
if err != nil {
66+
sendMsg(fmt.Sprintf("向OneDrive账户 `%s` 上传 `%s` 时出现连接问题,正在重试,当前为第%d次重试", username, filePath, errCount))
67+
} else {
68+
break
69+
}
70+
}
6071
if err != nil {
6172
log.Panicf("Failed to Restore :%v", err)
6273
}
@@ -81,10 +92,10 @@ func (rs *RestoreService) SimpleUploadToOriginalLoc(userId string, bearerToken s
8192
//@userId will be extracted as sent from the restore input xml
8293
//@filePath will be extracted from the file hierarchy the needs to be restored
8394
//@fileInfo it is the file info struct that contains the actual file reference and the size_type
84-
func (rs *RestoreService) SimpleUploadToAlternateLoc(altUserId string, bearerToken string, conflictOption string, filePath string, fileInfo fileutil.FileInfo, sendMsg func(text string), locText func(text string) string, username string) interface{} {
95+
func (rs *RestoreService) SimpleUploadToAlternateLoc(altUserId string, bearerToken string, targetFolder string, conflictOption string, filePath string, fileInfo fileutil.FileInfo, sendMsg func(text string), locText func(text string) string, username string) interface{} {
8596
if fileInfo.SizeType == fileutil.SizeTypeLarge {
8697
//For Large file type use resemble onedrive upload API
87-
return rs.recoverableUpload(altUserId, bearerToken, conflictOption, filePath, fileInfo, sendMsg, locText, username)
98+
return rs.recoverableUpload(altUserId, bearerToken, conflictOption, targetFolder, filePath, fileInfo, sendMsg, locText, username)
8899
} else {
89100

90101
uploadPath := fmt.Sprintf(simpleUploadPath, altUserId, filePath)

fileutil/file.go

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"io"
66
"os"
77
"path/filepath"
8+
"strconv"
89
"time"
910
)
1011

@@ -63,7 +64,7 @@ func GetAllUploadItemsFrmSource(sourcePath string) (map[string]FileInfo, error)
6364
}
6465

6566
//GetFilePartInBytes can returns the file in parts based on the provided offset
66-
func GetFilePartInBytes(buffer *[]byte,filePath string, startingOffset int64) error {
67+
func GetFilePartInBytes(buffer *[]byte, filePath string, startingOffset int64) error {
6768
file, err := os.Open(filePath)
6869
defer file.Close()
6970

@@ -87,7 +88,7 @@ func GetFilePartInBytes(buffer *[]byte,filePath string, startingOffset int64) er
8788
return fmt.Errorf("readAt: %v", err)
8889
}
8990
}
90-
return nil
91+
return nil
9192
}
9293

9394
//Returns the start offset chunk list based on the file size
@@ -171,3 +172,27 @@ func ReadFile(file *os.File) ([]byte, error) {
171172
}
172173
return buffer, nil
173174
}
175+
176+
func Byte2Readable(bytes float64) string {
177+
const kb float64 = 1024
178+
const mb = kb * 1024
179+
const gb = mb * 1024
180+
var readable float64
181+
var unit string
182+
_bytes := bytes
183+
184+
if _bytes >= gb {
185+
// xx GB
186+
readable = _bytes / gb
187+
unit = "GB"
188+
} else if _bytes < gb && _bytes >= mb {
189+
// xx MB
190+
readable = _bytes / mb
191+
unit = "MB"
192+
} else {
193+
// xx KB
194+
readable = _bytes / kb
195+
unit = "KB"
196+
}
197+
return strconv.FormatFloat(readable, 'f', 2, 64) + " " + unit
198+
}

main.go

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ func ApplyForNewPass(url string, ms int) string {
2525
return httpLocal.NewPassCheck(url, ms)
2626
}
2727

28-
func Upload(infoPath string, filePath string, threads int, sendMsg func() (func(text string), string, string), locText func(text string) string) {
28+
func Upload(infoPath string, filePath string, targetFolder string, threads int, sendMsg func() (func(text string), string, string), locText func(text string) string) {
2929

3030
programPath, err := filepath.Abs(filepath.Dir(infoPath))
3131
if err != nil {
@@ -59,7 +59,7 @@ func Upload(infoPath string, filePath string, threads int, sendMsg func() (func(
5959
} else {
6060
restore(restoreSrvc, fileInfoToUpload, threads)
6161
}*/
62-
restore(restoreSrvc, fileInfoToUpload, threads, sendMsg, locText, infoPath)
62+
restore(restoreSrvc, fileInfoToUpload, targetFolder, threads, sendMsg, locText, infoPath)
6363
err = os.Chdir(oldDir)
6464
if err != nil {
6565
log.Panic(err)
@@ -70,7 +70,7 @@ func changeBlockSize(MB int) {
7070
}
7171

7272
//Restore to original location
73-
func restore(restoreSrvc *upload.RestoreService, filesToRestore map[string]fileutil.FileInfo, threads int, sendMsg func() (func(text string), string, string), locText func(text string) string, infoPath string) {
73+
func restore(restoreSrvc *upload.RestoreService, filesToRestore map[string]fileutil.FileInfo, targetFolder string, threads int, sendMsg func() (func(text string), string, string), locText func(text string) string, infoPath string) {
7474
var wg sync.WaitGroup
7575
pool := make(chan struct{}, threads)
7676
for filePath, fileInfo := range filesToRestore {
@@ -96,7 +96,7 @@ func restore(restoreSrvc *upload.RestoreService, filesToRestore map[string]fileu
9696
temp(tip)
9797
userID, bearerToken := httpLocal.GetMyIDAndBearer(infoPath)
9898
username := strings.ReplaceAll(filepath.Base(infoPath), ".json", "")
99-
restoreSrvc.SimpleUploadToOriginalLoc(userID, bearerToken, "rename", filePath, fileInfo, temp, locText, username)
99+
restoreSrvc.SimpleUploadToOriginalLoc(userID, bearerToken, "rename", targetFolder, filePath, fileInfo, temp, locText, username)
100100

101101
//printResp(resp)
102102
defer temp("close")
@@ -119,7 +119,7 @@ func printResp(resp interface{}) {
119119
}
120120

121121
//Restore to Alternate location 还原到备用位置
122-
func restoreToAltLoc(restoreSrvc *upload.RestoreService, filesToRestore map[string]fileutil.FileInfo, sendMsg func() func(text string), locText func(text string) string, infoPath string) {
122+
func restoreToAltLoc(restoreSrvc *upload.RestoreService, filesToRestore map[string]fileutil.FileInfo, targetFolder string, sendMsg func() func(text string), locText func(text string) string, infoPath string) {
123123
rootFolder := fileutil.GetAlternateRootFolder()
124124
var wg sync.WaitGroup
125125
pool := make(chan struct{}, 10)
@@ -137,7 +137,7 @@ func restoreToAltLoc(restoreSrvc *upload.RestoreService, filesToRestore map[stri
137137
us := ""
138138
userID, bearerToken := httpLocal.GetMyIDAndBearer(infoPath)
139139
//username := strings.ReplaceAll(filepath.Base(infoPath), ".json", "")
140-
restoreSrvc.SimpleUploadToAlternateLoc(userID, bearerToken, "rename", rootFilePath, fileItem, temp, locText, us)
140+
restoreSrvc.SimpleUploadToAlternateLoc(userID, bearerToken, "rename", targetFolder, rootFilePath, fileItem, temp, locText, us)
141141

142142
}()
143143
wg.Wait()
@@ -197,6 +197,18 @@ func botSend(botKey string, iuserID string, initText string) func(string) {
197197

198198
}
199199
}
200+
201+
func DirSize(path string) (int64, error) {
202+
var size int64
203+
err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error {
204+
if !info.IsDir() {
205+
size += info.Size()
206+
}
207+
return err
208+
})
209+
return size, err
210+
}
211+
200212
func main() {
201213
var codeURL string
202214
var configFile string
@@ -206,6 +218,7 @@ func main() {
206218
var iuserID string
207219
var ms int
208220
var block int
221+
var targetFolder string
209222
// StringVar用指定的名称、控制台参数项目、默认值、使用信息注册一个string类型flag,并将flag的值保存到p指向的变量
210223
flag.StringVar(&codeURL, "a", "", "通过登录 https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=ad5e65fd-856d-4356-aefc-537a9700c137&response_type=code&redirect_uri=http://localhost/onedrive-login&response_mode=query&scope=offline_access%20User.Read%20Files.ReadWrite.All 后跳转的网址(输入网址时需要使用双引号包裹网址)")
211224
flag.IntVar(&ms, "v", 0, "选择版本,其中0为国际版,1为个人版(家庭版),默认为0")
@@ -215,6 +228,7 @@ func main() {
215228
flag.IntVar(&block, "b", 10, "自定义上传分块大小, 可以提高网络吞吐量, 受限于磁盘性能和网络速度,默认为 10 (单位MB)")
216229
flag.StringVar(&botKey, "tgbot", "", "使用Telegram机器人实时监控上传,此处需填写机器人的access token,形如123456789:xxxxxxxxx,输入时需使用双引号包裹")
217230
flag.StringVar(&iuserID, "uid", "", "使用Telegram机器人实时监控上传,此处需填写接收人的userID,形如123456789")
231+
flag.StringVar(&targetFolder, "r", "", "设置上传的目录")
218232

219233
// 从arguments中解析注册的flag。必须在所有flag都注册好而未访问其值时执行。未注册却使用flag -help时,会返回ErrHelp。
220234

@@ -224,12 +238,18 @@ func main() {
224238
startTime := time.Now().Unix()
225239
writer := uilive.New()
226240
writer.Start()
241+
size, err := DirSize(folder)
242+
if err != nil {
243+
log.Panic(err)
244+
}
245+
_, _ = fmt.Fprintf(writer, "`%s` 开始上传,大小: `%s`", folder, fileutil.Byte2Readable(float64(size)))
246+
227247
var sendMsg func(string)
228248
if botKey != "" && iuserID != "" {
229-
sendMsg = botSend(botKey, iuserID, fmt.Sprintf("`%s` 开始上传", folder))
249+
sendMsg = botSend(botKey, iuserID, fmt.Sprintf("`%s` 开始上传,大小: `%s`", folder, fileutil.Byte2Readable(float64(size))))
230250
}
231251

232-
Upload(strings.ReplaceAll(configFile, "\\", "/"), strings.ReplaceAll(folder, "\\", "/"), thread, func() (func(text string), string, string) {
252+
Upload(strings.ReplaceAll(configFile, "\\", "/"), strings.ReplaceAll(folder, "\\", "/"), targetFolder, thread, func() (func(text string), string, string) {
233253
if botKey != "" && iuserID != "" {
234254
return func(text string) {
235255
_, _ = fmt.Fprintf(writer, "%s\n", text)
@@ -243,9 +263,11 @@ func main() {
243263
}, func(text string) string {
244264
return oldFunc(text)
245265
})
246-
_, _ = fmt.Fprintf(writer, "%s上传完成,耗时 %d 秒\n", folder, time.Now().Unix()-startTime)
266+
cost := time.Now().Unix() - startTime
267+
speed := fileutil.Byte2Readable(float64(size) / float64(cost))
268+
_, _ = fmt.Fprintf(writer, "%s上传完成,耗时 %d 秒,平均上传速度 %s/s\n", folder, cost, speed)
247269
if botKey != "" && iuserID != "" {
248-
sendMsg(fmt.Sprintf("`%s`上传完成,耗时 %d 秒\n", folder, time.Now().Unix()-startTime))
270+
sendMsg(fmt.Sprintf("`%s`上传完成,耗时 %d 秒,平均上传速度 `%s/s`", folder, cost, speed))
249271
}
250272
} else {
251273
if codeURL == "" {
@@ -257,6 +279,7 @@ func main() {
257279

258280
// 打印
259281
//fmt.Printf("username=%v password=%v host=%v port=%v", username, password, host, port)
282+
260283
}
261284

262285
/*

0 commit comments

Comments
 (0)