Skip to content

Commit 896a110

Browse files
committed
feat: 采用异步任务处理大文件传输 (fix #91)
- 添加 rclone 异步任务 API 支持 (_async=true) - 实现任务状态轮询和等待机制 - 支持 AbortSignal 取消任务 - 超时时自动停止 rclone 任务 - 优化错误处理,添加参数验证 - 将 copyDir/moveDir/sync 改为异步执行 - 避免大文件传输时的 HTTP 超时问题 参考: https://rclone.org/rc/#running-asynchronous-jobs-with-async-true
1 parent 33633d2 commit 896a110

4 files changed

Lines changed: 298 additions & 58 deletions

File tree

src/controller/storage/storage.ts

Lines changed: 88 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@ import { hooks } from '../../services/hook'
22
import { rcloneInfo } from '../../services/rclone'
33
import { FileInfo, StorageList, StorageSpace } from '../../type/rclone/rcloneInfo'
44
import { ParametersType } from '../../type/defaults'
5-
import { getRcloneApiHeaders, rclone_api_post } from '../../utils/rclone/request'
5+
import {
6+
getRcloneApiHeaders,
7+
rclone_api_post,
8+
rclone_api_exec_async,
9+
} from '../../utils/rclone/request'
610
import { openlist_api_get, openlist_api_post } from '../../utils/openlist/request'
711
import { formatPath } from '../../utils/utils'
812
import { openlistInfo } from '../../services/openlist'
@@ -417,14 +421,28 @@ async function copyDir(
417421
destStoragename: string,
418422
destPath: string
419423
) {
420-
await rclone_api_post(
421-
'/sync/copy',
422-
{
423-
srcFs: convertStoragePath(storageName, path, true),
424-
dstFs: convertStoragePath(destStoragename, destPath, true) + getFileName(path),
425-
},
426-
true
427-
)
424+
// 参数验证
425+
if (!storageName || !destStoragename) {
426+
throw new Error('Source or destination storage name is empty')
427+
}
428+
if (!path) {
429+
throw new Error('Source path is empty')
430+
}
431+
432+
const srcFs = convertStoragePath(storageName, path, true)
433+
const dstFs = convertStoragePath(destStoragename, destPath, true) + getFileName(path)
434+
435+
if (!srcFs || !dstFs) {
436+
throw new Error('Invalid source or destination path')
437+
}
438+
439+
const success = await rclone_api_exec_async('/sync/copy', {
440+
srcFs,
441+
dstFs,
442+
})
443+
if (!success) {
444+
throw new Error(`Copy directory failed: ${srcFs} -> ${dstFs}`)
445+
}
428446
}
429447

430448
async function moveDir(
@@ -434,16 +452,29 @@ async function moveDir(
434452
destPath: string,
435453
newNmae?: string
436454
) {
437-
await rclone_api_post(
438-
'/sync/move',
439-
{
440-
srcFs: convertStoragePath(storageName, path, true),
441-
dstFs:
442-
convertStoragePath(destStoragename, destPath, true) +
443-
(newNmae ? newNmae : getFileName(path)),
444-
},
445-
true
446-
)
455+
// 参数验证
456+
if (!storageName || !destStoragename) {
457+
throw new Error('Source or destination storage name is empty')
458+
}
459+
if (!path) {
460+
throw new Error('Source path is empty')
461+
}
462+
463+
const srcFs = convertStoragePath(storageName, path, true)
464+
const dstFs =
465+
convertStoragePath(destStoragename, destPath, true) + (newNmae ? newNmae : getFileName(path))
466+
467+
if (!srcFs || !dstFs) {
468+
throw new Error('Invalid source or destination path')
469+
}
470+
471+
const success = await rclone_api_exec_async('/sync/move', {
472+
srcFs,
473+
dstFs,
474+
})
475+
if (!success) {
476+
throw new Error(`Move directory failed: ${srcFs} -> ${dstFs}`)
477+
}
447478
}
448479

449480
//sync,需完整path(pathF2f)
@@ -455,26 +486,46 @@ async function sync(
455486
bisync?: boolean
456487
) {
457488
//bisync:双向同步
489+
490+
// 参数验证
491+
if (!storageName || !destStoragename) {
492+
throw new Error('Source or destination storage name is empty')
493+
}
494+
if (!path || !destPath) {
495+
throw new Error('Source or destination path is empty')
496+
}
497+
498+
let success: boolean
458499
if (!bisync) {
459-
await rclone_api_post(
460-
'/sync/sync',
461-
{
462-
//同步
463-
srcFs: convertStoragePath(storageName, path, true),
464-
dstFs: convertStoragePath(destStoragename, destPath, true),
465-
},
466-
true
467-
)
500+
const srcFs = convertStoragePath(storageName, path, true)
501+
const dstFs = convertStoragePath(destStoragename, destPath, true)
502+
503+
if (!srcFs || !dstFs) {
504+
throw new Error('Invalid source or destination path')
505+
}
506+
507+
success = await rclone_api_exec_async('/sync/sync', {
508+
srcFs,
509+
dstFs,
510+
})
511+
if (!success) {
512+
throw new Error(`Sync failed: ${srcFs} -> ${dstFs}`)
513+
}
468514
} else {
469-
await rclone_api_post(
470-
'/sync/bisync',
471-
{
472-
//双向同步
473-
path1: convertStoragePath(storageName, path, true),
474-
path2: convertStoragePath(destStoragename, destPath, true),
475-
},
476-
true
477-
)
515+
const path1 = convertStoragePath(storageName, path, true)
516+
const path2 = convertStoragePath(destStoragename, destPath, true)
517+
518+
if (!path1 || !path2) {
519+
throw new Error('Invalid source or destination path')
520+
}
521+
522+
success = await rclone_api_exec_async('/sync/bisync', {
523+
path1,
524+
path2,
525+
})
526+
if (!success) {
527+
throw new Error(`Bidirectional sync failed: ${path1} <-> ${path2}`)
528+
}
478529
}
479530
}
480531

src/controller/task/runner.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { copyDir, copyFile, delDir, delFile, moveDir, moveFile, sync } from '../
55
async function runTask(task: TaskListItem): Promise<TaskListItem> {
66
let taskMsg: string = task.runInfo.msg || ''
77

8-
const executeTask = (task: TaskListItem) => {
8+
const executeTask = async (task: TaskListItem) => {
99
console.log(`Executing ${task.taskType} task: ${task.name}`)
1010

1111
const srcIsDir = task.source.path.endsWith('/')
@@ -30,15 +30,15 @@ async function runTask(task: TaskListItem): Promise<TaskListItem> {
3030
//复制
3131
if (srcIsDir && targetIsDir) {
3232
//复制目录
33-
copyDir(
33+
await copyDir(
3434
task.source.storageName,
3535
task.source.path,
3636
task.target.storageName,
3737
task.target.path
3838
)
3939
} else if (!srcIsDir && !targetIsDir) {
4040
//复制文件
41-
copyFile(
41+
await copyFile(
4242
task.source.storageName,
4343
task.source.path,
4444
task.target.storageName,
@@ -47,7 +47,7 @@ async function runTask(task: TaskListItem): Promise<TaskListItem> {
4747
)
4848
} else if (!srcIsDir && targetIsDir) {
4949
//复制文件到目录
50-
copyFile(
50+
await copyFile(
5151
task.source.storageName,
5252
task.source.path,
5353
task.target.storageName,
@@ -62,23 +62,23 @@ async function runTask(task: TaskListItem): Promise<TaskListItem> {
6262
//移动
6363
if (srcIsDir && targetIsDir) {
6464
//移动目录
65-
moveDir(
65+
await moveDir(
6666
task.source.storageName,
6767
task.source.path,
6868
task.target.storageName,
6969
task.target.path
7070
)
7171
} else if (!srcIsDir && targetIsDir) {
7272
//移动文件到目录
73-
moveFile(
73+
await moveFile(
7474
task.source.storageName,
7575
task.source.path,
7676
task.target.storageName,
7777
task.target.path
7878
)
7979
} else if (!srcIsDir && !targetIsDir) {
8080
//移动文件
81-
moveFile(
81+
await moveFile(
8282
task.source.storageName,
8383
task.source.path,
8484
task.target.storageName,
@@ -95,21 +95,21 @@ async function runTask(task: TaskListItem): Promise<TaskListItem> {
9595
//删除
9696
if (srcIsDir) {
9797
//删除目录
98-
delDir(task.source.storageName, task.source.path)
98+
await delDir(task.source.storageName, task.source.path)
9999
} else {
100100
//删除文件
101-
delFile(task.source.storageName, task.source.path)
101+
await delFile(task.source.storageName, task.source.path)
102102
}
103103
break
104104
}
105105
case 'sync': {
106106
//同步
107-
sync(task.source.storageName, task.source.path, task.target.storageName, task.target.path)
107+
await sync(task.source.storageName, task.source.path, task.target.storageName, task.target.path)
108108
break
109109
}
110110
case 'bisync': {
111111
//双向同步
112-
sync(
112+
await sync(
113113
task.source.storageName,
114114
task.source.path,
115115
task.target.storageName,
@@ -131,7 +131,7 @@ async function runTask(task: TaskListItem): Promise<TaskListItem> {
131131

132132
try {
133133
if (task.enable) {
134-
executeTask(task)
134+
await executeTask(task)
135135
task.runInfo = { ...task.runInfo, error: false, msg: taskMsg }
136136
}
137137
} catch (error) {

src/page/storage/explorer.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -355,22 +355,22 @@ function ExplorerItem() {
355355
droplist={
356356
<Menu>
357357
<Menu.Item
358-
onClick={() => {
359-
clipList.forEach(item => {
358+
onClick={async () => {
359+
for (const item of clipList) {
360360
if (item.isMove) {
361361
if (item.isDir) {
362-
moveDir(item.storageName, item.path, storageName!, path!)
362+
await moveDir(item.storageName, item.path, storageName!, path!)
363363
} else {
364-
moveFile(item.storageName, item.path, storageName!, path!)
364+
await moveFile(item.storageName, item.path, storageName!, path!)
365365
}
366366
} else {
367367
if (item.isDir) {
368-
copyDir(item.storageName, item.path, storageName!, path!)
368+
await copyDir(item.storageName, item.path, storageName!, path!)
369369
} else {
370-
copyFile(item.storageName, item.path, storageName!, path!)
370+
await copyFile(item.storageName, item.path, storageName!, path!)
371371
}
372372
}
373-
})
373+
}
374374
setClipList([])
375375
Notification.success({
376376
title: t('success'),

0 commit comments

Comments
 (0)