rclone 编译为 WASM:在浏览器中以 JavaScript 库方式调用 rclone RC 接口
2026/9/8 21:51:41 网站建设 项目流程

rclone 编译为 WASM:在浏览器中以 JavaScript 库方式调用 rclone RC 接口

【免费下载链接】rclone"rsync for cloud storage" - Google Drive, S3, Dropbox, Backblaze B2, One Drive, Swift, Hubic, Wasabi, Google Cloud Storage, Azure Blob, Azure Files, Yandex Files项目地址: https://gitcode.com/GitHub_Trending/rc/rclone

rclone 的 fs/rc/js/ 目录提供了一套完整的"WebAssembly 化"示例:通过GOOS=js GOARCH=wasm将 rclone 的 rc(remote control)核心编译为rclone.wasm,再借助 JavaScript 加载器在浏览器中直接调用 rclone 的 rc 方法(如core/versionoperations/mkdir),从而把 rclone 变成可在 Web 页面内运行的存储操作库。读完本文,你将理解这套编译、加载、调用的完整链路:从 Go 侧通过syscall/js导出rc全局函数,到 JS 侧用 Promise 等待 WASM 就绪并发起调用,再到错误状态码如何从 Go 错误类型映射为 HTTP 语义状态。

目录结构与各文件职责

fs/rc/js/README.md 对该目录的文件清单有明确说明,实际目录内容与其一致:

文件作用
index.html测试网页,仅加载loader.js并提示"check the console"
loader.js加载 WASM 模块的 JavaScript,文末附带用法示例
main.go导出 rclone rc 的 Go 主代码(构建标签js
Makefile测试用 makefile,定义buildserve两个目标
serve.go测试用静态文件服务器,在localhost:3000上提供页面
wasm_exec.js来自 Go 源码的官方运行时接口代码(不要修改)

index.html的实现非常精简——页面本身几乎没有任何 DOM 内容,一切交互都发生在控制台:

<!doctype html> <html> <head> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Rclone</title> </head> <body> <script src="loader.js"></script> <p>Welcome to rclone - check the console</p> </body> </html>

编译:两步 Makefile 与等价的手动命令

fs/rc/js/README.md 给出的编译方式有两种,二者等价:

make # 或者手动执行: GOARCH=wasm GOOS=js go build -o rclone.wasm

Makefile 中定义的两个目标完整内容如下:

build: GOARCH=wasm GOOS=js go build -o rclone.wasm serve: build go run serve.go

其中build目标产出rclone.wasm二进制;serve目标先依赖build,再运行 serve.go。serve.go//go:build none标签(因此不会被go build误编入 WASM 目标),其逻辑是:

  1. 注册 MIME 类型.wasm -> application/wasm.js -> application/javascript——这一步对WebAssembly.instantiateStreaming正确识别响应类型至关重要;
  2. http.FileServer把当前目录挂到根路径,监听:3000,控制台会打印Serving on http://localhost:3000/

加载链路:loader.js 如何启动 WASM

loader.js 是整个浏览器端的入口,完整流程分三步:

  1. 声明就绪信号:在文件头部创建一个 Promise,Go 侧编译产物启动成功后会调用rcValidResolve将其 resolve:
var rcValidResolve var rcValid = new Promise(resolve => { rcValidResolve = resolve });
  1. 注入 wasm_exec.js 并实例化:动态插入<script src="wasm_exec.js">,在其加载完成后,对缺失WebAssembly.instantiateStreaming的旧浏览器做 polyfill(先取 arrayBuffer 再WebAssembly.instantiate),然后new Go()并以fetch("rclone.wasm")流式实例化:
script.onload = function () { if (!WebAssembly.instantiateStreaming) { // polyfill WebAssembly.instantiateStreaming = async (resp, importObject) => { const source = await (await resp).arrayBuffer(); return await WebAssembly.instantiate(source, importObject); }; } const go = new Go(); WebAssembly.instantiateStreaming(fetch("rclone.wasm"), go.importObject).then((result) => { go.run(result.instance); }); };
  1. 等待就绪后调用 rc:在rcValid.then(...)回调中演示四个调用示例:
rcValid.then(() => { // The rc call takes two parameters, method and input object and // returns an output object. // If the output object has an "error" and a "status" then it is an // error (it would be nice to signal this out of band). console.log("core/version", rc("core/version", null)) console.log("rc/noop", rc("rc/noop", {"string":"one",number:2})) console.log("operations/mkdir", rc("operations/mkdir", {"fs":":memory:","remote":"bucket"})) console.log("operations/list", rc("operations/list", {"fs":":memory:","remote":"bucket"})) })

按 fs/rc/js/README.md 的"Running"章节,运行make serve后访问 http://localhost:3000/,打开浏览器 JavaScript 控制台即可看到这四行 rc 调用的输出;loader.js末尾就是官方推荐的用法参照。

Go 侧实现:main.go 导出的 rc 全局函数

main.go 带//go:build js构建标签,是 WASM 侧的"胶水层"。

依赖引入决定了能力边界

main.go 的 import 块决定了这个 WASM 库"装了什么":

import ( ... "github.com/rclone/rclone/fs" "github.com/rclone/rclone/fs/rc" // Core functionality we need _ "github.com/rclone/rclone/fs/operations" _ "github.com/rclone/rclone/fs/sync" // _ "github.com/rclone/rclone/backend/all" // import all backends // Backends _ "github.com/rclone/rclone/backend/memory" )
  • fs/operationsfs/sync的空导入触发了其中rc.Add的注册,因此 WASM 内可用operations/*(mkdir、list 等)与 sync 相关的 rc 方法;
  • 注释掉的backend/all表明默认只引入backend/memory这一个内存后端(示例中即使用:memory:作为fs);若需支持其他后端,可在此处按需替换空导入,再重新执行GOARCH=wasm GOOS=js go build -o rclone.wasm。从源码结构看,由于云存储后端普遍依赖网络 OAuth 流程,浏览器内可直接使用的仍是 memory 这类无需外部凭证的后端。

main() 的初始化序列

main()依次完成四件事:

  1. 检查js.Global()documentJSON是否存在,缺失则log.Fatalf(说明未运行在浏览器中);
  2. 导出核心 APIjs.Global().Set("rc", js.FuncOf(rcCallback))—— 这正是 loader.js 中可直接调用的全局函数rc(method, input)
  3. 调用宿主页面预定义的rcValidResolve,通知 loader.js 的 Promise"模块已就绪",与 loader.js 第 3–5 行的信号机制一一对应;
  4. select {}永久阻塞,让 Go 运行时常驻以响应后续的 JS 调用。

rcCallback:一次调用的完整生命周期

rcCallback是浏览器与 rclone 之间的唯一入口,签名固定为rc(method, in),处理链如下:

  1. 参数校验:必须恰好 2 个参数,args[0]为方法名(字符串),args[1]为输入,只能是null或 JS 对象;对象会先经JSON.stringify序列化,再json.Unmarshal成 rclone 的rc.Params(其本质是 Params 类型,即map[string]any);
  2. 方法路由rc.Calls.Get(method)在 全局注册表 中按路径查找Call。注册表由rc.Add(call)填充——loader.js 示例里的core/version即注册于 fs/rc/internal.go 的init()rc/noop同样在 fs/rc/internal.go 注册;
  3. 执行call.Fn(ctx, in)得到rc.Params输出,非 nil 时经过rc.Reshape(通过一轮 JSON 序列化/反序列化把输出归一为纯map[string]interface{},保证可安全转换为 JS 值),最终js.ValueOf(out2)返回给浏览器。

需要说明的两个源码内标记:回调使用context.Background()并带有// FIXME注释;函数注释中也有FIXME should this should return a promise so we can return errors properly?——因此错误目前是通过返回值内携带errorstatus字段来传递,而非抛出 JS 异常(loader.js 的注释同样点明了这一点:"If the output object has an 'error' and a 'status' then it is an error")。

错误对象的形状与状态码映射

出错时errorValue返回如下结构(与 fs/rc/params.go 中rc.Error生成的标准错误响应保持同构):

return js.ValueOf(map[string]interface{}{ "status": status, "error": err.Error(), "input": in, "path": method, })

状态码映射规则为:默认500 Internal Server Error;当错误是fs.ErrorDirNotFound/fs.ErrorObjectNotFound时改为404 Not Found;当错误是rc.ErrParamInvalid/rc.ErrParamNotFound时改为400 Bad Request。这与 rclone HTTP RC 服务端的行为一致,方便 Web 端用同一套错误处理逻辑。

rc 方法体系:WASM 与 HTTP 共用同一套注册机制

理解本目录的关键在于:WASM 库暴露的并不是私有 API,而是与rclone rcd/--rc完全相同的 rc 注册表。每个CallPath(方法名)、Fnfunc(ctx, in Params) (out Params, err error))、NoAuth(是否免鉴权)等字段;WASM 路径下没有 HTTP 鉴权环节,NoAuth等属性不影响调用,但方法集合完全一致。以示例中的core/version为例,其 Help 列出了versiondecomposedisGitosarchgoVersion等返回字段——浏览器控制台打印的输出即包含这些键。

此外,rclone 全局 rc 相关配置(--rc--rc-addr默认localhost:5572--rc-enable-metrics等,见 fs/rc/rc.go)属于 HTTP 服务端模式;WASM 库路径不启动 HTTP 服务器,这些选项在浏览器场景下不生效,这也是两种"使用 rc 的方式"的本质区别:一种是进程内库调用,一种是网络请求。

上手步骤与限制说明

完整上手路径(在 fs/rc/js/ 目录下操作):

make serve

该命令先编译rclone.wasm再启动静态服务器,随后浏览器打开 http://localhost:3000/,在开发者控制台查看core/versionrc/noopoperations/mkdiroperations/list四个示例输出的对象。

适用前提与限制:

  • 需要 Go 工具链(编译产物为原生go build输出,未使用 TinyGo 等第三方工具);
  • 当前示例仅引入backend/memory后端(见 main.go 的导入注释),示例数据全部落在:memory:,刷新页面即丢失;
  • rc 调用是同步式的 JS 函数返回,错误靠返回值中的status/error字段判断(如上文FIXME注释所述);
  • 所有 rc 方法均可通过同一注册表机制被发现与扩展——operations/*sync/*等已在默认构建中可用。

这套 "rclone as a library in the browser" 的最小可用参考实现,为在 Web 前端直接执行 rclone 的目录创建、列举、同步等操作提供了完整的编译与调用样板。

【免费下载链接】rclone"rsync for cloud storage" - Google Drive, S3, Dropbox, Backblaze B2, One Drive, Swift, Hubic, Wasabi, Google Cloud Storage, Azure Blob, Azure Files, Yandex Files项目地址: https://gitcode.com/GitHub_Trending/rc/rclone

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询