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/version、operations/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,定义build与serve两个目标 |
| 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.wasmMakefile 中定义的两个目标完整内容如下:
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 目标),其逻辑是:
- 注册 MIME 类型
.wasm -> application/wasm、.js -> application/javascript——这一步对WebAssembly.instantiateStreaming正确识别响应类型至关重要; - 用
http.FileServer把当前目录挂到根路径,监听:3000,控制台会打印Serving on http://localhost:3000/。
加载链路:loader.js 如何启动 WASM
loader.js 是整个浏览器端的入口,完整流程分三步:
- 声明就绪信号:在文件头部创建一个 Promise,Go 侧编译产物启动成功后会调用
rcValidResolve将其 resolve:
var rcValidResolve var rcValid = new Promise(resolve => { rcValidResolve = resolve });- 注入 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); }); };- 等待就绪后调用 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/operations、fs/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()依次完成四件事:
- 检查
js.Global()、document、JSON是否存在,缺失则log.Fatalf(说明未运行在浏览器中); - 导出核心 API:
js.Global().Set("rc", js.FuncOf(rcCallback))—— 这正是 loader.js 中可直接调用的全局函数rc(method, input); - 调用宿主页面预定义的
rcValidResolve,通知 loader.js 的 Promise"模块已就绪",与 loader.js 第 3–5 行的信号机制一一对应; select {}永久阻塞,让 Go 运行时常驻以响应后续的 JS 调用。
rcCallback:一次调用的完整生命周期
rcCallback是浏览器与 rclone 之间的唯一入口,签名固定为rc(method, in),处理链如下:
- 参数校验:必须恰好 2 个参数,
args[0]为方法名(字符串),args[1]为输入,只能是null或 JS 对象;对象会先经JSON.stringify序列化,再json.Unmarshal成 rclone 的rc.Params(其本质是 Params 类型,即map[string]any); - 方法路由:
rc.Calls.Get(method)在 全局注册表 中按路径查找Call。注册表由rc.Add(call)填充——loader.js 示例里的core/version即注册于 fs/rc/internal.go 的init(),rc/noop同样在 fs/rc/internal.go 注册; - 执行:
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?——因此错误目前是通过返回值内携带error与status字段来传递,而非抛出 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 注册表。每个Call含Path(方法名)、Fn(func(ctx, in Params) (out Params, err error))、NoAuth(是否免鉴权)等字段;WASM 路径下没有 HTTP 鉴权环节,NoAuth等属性不影响调用,但方法集合完全一致。以示例中的core/version为例,其 Help 列出了version、decomposed、isGit、os、arch、goVersion等返回字段——浏览器控制台打印的输出即包含这些键。
此外,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/version、rc/noop、operations/mkdir、operations/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),仅供参考