C++ 中使用 librclone

图片来源:使用 Aseprite 制作

rclone 虽然是用 Go 写的但官方支持编译为 C 库,在代码中通过 rclone 的 远程控制 API 进行通信

编译

git clone https://github.com/rclone/rclone.git

// 编译为Linux静态库
go -C rclone build --buildmode=c-archive -o librclone.a github.com/rclone/rclone/librclone

// 或者也可以编译为Linux动态库
go -C rclone  build --buildmode=c-shared -o librclone.so github.com/rclone/rclone/librclone 

编译完成后在 rclone 目录下会生成 librclone.h 和 librclone.a(动态库则为 librclone.so)

librclone.h

librclone.h 代码十分简单,要关注的只有一个结构体和四个函数

初始化和资源释放

// 初始化 librclone, 在使用 RcloneRPC 前调用
extern void RcloneInitialize();

// 资源释放,程序退出前或不再使用 librclone 后调用
extern void RcloneFinalize();

RcloneRPC

要控制 librclone 的行为唯一要做的就是调用 RcloneRPC 发送指令

// method 是 https://rclone.org/rc/ 中定义的方法名
// input 则是一个 json 结构
extern struct RcloneRPCResult RcloneRPC(char* method, char* input);
// RcloneRPC 返回的结构体,其中 Output 需要用户调用 free 或者 RcloneFreeString 释放
// 其中 Status 的值为 200 表示指令执行成功,否则为失败
// Output 是一个 json 结构,内容为部分指令的返回结果,或失败信息
struct RcloneRPCResult {
	char*	Output;
	int	Status;
};

extern void RcloneFreeString(char* str);

样例

Linux 用户如何从 VRPirates 手动安装下载 Meta Quest 游戏 中的两条 rclone 命令为例,将他们翻译为等效的 C++ 代码

下载单个文件

rclone sync ":http:/meta.7z" . --http-url  "https://theapp.vrrookie.xyz/" --tpslimit 1.0 --tpslimit-burst 3
#include <iostream>
#include "librclone.h"

int main(int, char **)
{
    RcloneInitialize();
    // https://rclone.org/rc/#operations-copyfile
    char rc_method[] = "operations/copyfile";
    char rc_input[] = R"({
        "srcFs": ":http,url='https://theapp.vrrookie.xyz/':",
        "srcRemote": "/meta.7z",
        "dstFs": ".",
        "dstRemote": "meta.7z",
        "_config": {
                "TPSLimit": 1.0,
                "TPSLimitBurst": 3,
                "UserAgent": "rclone/v1.65.2"
        }
    })";

    RcloneRPCResult result = RcloneRPC(rc_method, rc_input);
    if(result.Status != 200)
    {
        std::cerr << "Error: " << result.Output << std::endl;
    }

    RcloneFreeString(result.Output);
    RcloneFinalize();

    return 0;
}

下载文件夹

rclone copy ":http:/d251dd0aa665a8c29a6c1116ed1a3f5f"  .  --transfers 1 --multi-thread-streams 0 --progress --rc  --http-url  "https://theapp.vrrookie.xyz/" --tpslimit 1.0 --tpslimit-burst 3
#include <iostream>
#include "librclone.h"

int main(int, char **)
{
    RcloneInitialize();

    // https://rclone.org/rc/#sync-copy
    char rc_method[] = "sync/copy";
    char rc_input[] = R"({
        "srcFs": ":http,url='https://theapp.vrrookie.xyz':/d251dd0aa665a8c29a6c1116ed1a3f5f",
        "dstFs": ".",
        "_config": {
                "Transfers": 1,
                "TPSLimit": 1.0,
                "TPSLimitBurst": 3,
                "MultiThreadStreams": 0,
                "UserAgent": "rclone/v1.65.2"
        }
    })";

    RcloneRPCResult result = RcloneRPC(rc_method, rc_input);
    if (result.Status != 200)
    {
        std::cerr << "Error: " << result.Output << std::endl;
    }

    RcloneFreeString(result.Output);
    RcloneFinalize();

    return 0;
}

异步下载

上面展示的程序都是阻塞等待下载完成,要实际上只需要在 input 中添加 "_async": true 即可实现异步下载,并通过其他指令查询下载进度

为简化代码未做错误处理,也未实现 getJsonValueByKeyjsonCreate 两个函数

#include <iostream>
#include <unistd.h>
#include "librclone.h"

int main(int, char **)
{
    RcloneInitialize();

    // https://rclone.org/rc/#sync-copy
    char rc_method[] = "sync/copy";
    // 增加 _async 参数,使得 rclone 后台运行,RcloneRPC会立即返回
    char rc_input[] = R"({
        "srcFs": ":http,url='https://theapp.vrrookie.xyz':/d251dd0aa665a8c29a6c1116ed1a3f5f",
        "dstFs": "local_dir",
        "_async": true,
        "_config": {
                "Transfers": 1,
                "TPSLimit": 1.0,
                "TPSLimitBurst": 3,
                "MultiThreadStreams": 0,
                "UserAgent": "rclone/v1.65.2"
        }
    })";

    RcloneRPCResult result = RcloneRPC(rc_method, rc_input);

    // 通过解析 result 可获取 jobid
    int jobid = getJsonValueByKey(result.Output, "jobid");
    RcloneFreeString(result.Output);

    while (true)
    {
        sleep(1);
        // 假设 jobid 为 1,jsonCreate 构造结果 {"jobid":1}
        RcloneRPCResult job_status = RcloneRPC("job/status", jsonCreate("jobid", jobid));

        // 通过解析 job_status 可获取 group 和 finished
        // finished用来判断是否下载完成
        const char *group = getJsonValueByKey(job_status.Output, "group");
        bool isfinished = getJsonValueByKey(job_status.Output, "finished");
        if (isfinished)
        {
            break;
        }

        // 通过解析 core/stats 可获取下载状态,包括下载速度、下载进度等
        // 假设 group 值为 "job/1", jsonCreate 构造结果 {"group":"job/1"}
        RcloneRPCResult stats_result = RcloneRPC("core/stats", jsonCreate("group", group));
        std::cout << "Stats: " << stats_result.Output << std::endl;

        RcloneFreeString(job_status.Output);
        RcloneFreeString(stats_result.Output);
    }
    
    RcloneFinalize();

    return 0;
}


参考:

知识共享许可协议
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

发表评论

您的电子邮箱地址不会被公开。 必填项已用 * 标注