base64-cj 使用指南:ArkTS 调用 Cangjie 版 Base64 编解码算法实战
base64-cj 使用指南:ArkTS 调用 Cangjie 版 Base64 编解码算法实战
本指南以 library/README.md 为主体,结合仓库源码(library/src/main/ets/components/MainPage.ets、library/src/main/cangjie/index.cj 等)展开,完整覆盖 base64-cj 的对外接口、安装接入方式、功能示例、实现原理与约束条件,帮助开发者在 HarmonyOS 应用工程中快速引入并理解这一套「ArkTS 调用 Cangjie 版 Base64」的编解码方案。
base64-cj 是一个以 OpenHarmony HAR 库形式发布的开源组件,核心价值是让 ArkTS 开发者通过简洁的函数调用,直接复用 Cangjie 语言标准库 encoding.base64 实现的 Base64 编解码算法。读完本文,你将掌握该库的全部 5 个对外 API、两种安装接入方式(中心仓安装与本地 module 引入)、两套完整可运行的功能示例,并能从源码层面理解 ArkTS 与 Cangjie 的互操作调用链。
项目概览与特性
base64-cj 的定位非常聚焦:ArkTS 调用 Cangjie 版 base64 编解码算法实现(见 library/README.md 的「介绍」与 library/oh-package.json5 中的包描述)。其核心特性是:
- 支持 ArkTS 调用 Cangjie 版 base64 编解码,对外暴露字符串与字节数组两套数据形态的编码、解码能力。
源码目录结构
仓库采用典型的多模块 HarmonyOS 工程布局,README 给出了如下目录组织:
├─ AppScope
├─ entry # 示例代码
├─ hvigor # 构建工具
├─ library # 源码文件夹
│ └─ src
│ └─ main
│ ├─ cangjie # cangjie 和 ArkTS 互操作代码
│ ├─ ets # ets 对外接口
│ └─ resources
└─ README.md # 库使用介绍
结合源码可以进一步明确各模块职责:
- library:HAR 库源码模块,类型为
har(见 library/src/main/module.json5),对外包名为@cangjie-tpc/base64(见 library/oh-package.json5); - library/src/main/cangjie:存放 Cangjie 源码、互操作 API 声明与加载器(见 library/src/main/cangjie/index.cj、library/src/main/cangjie/ark_interop_api/ark_interop_api.d.ts、library/src/main/cangjie/loader/libark_interop_loader.d.ts);
- library/src/main/ets/components:ArkTS 侧的对外封装,即导出函数的真实实现(见 library/src/main/ets/components/MainPage.ets);
- library/Index.ets:库的统一导出入口,将 5 个函数从
./src/main/ets/components/MainPage再导出(见 library/Index.ets); - entry:示例应用,包含 5 个可跳转的示例页面,入口为 entry/src/main/ets/pages/Index.ets。
对外接口说明
base64-cj 对外共暴露 5 个函数,覆盖两种典型场景:字符串数据编解码 与 字节(ArrayBuffer/Uint8Array)数据编解码。README 中的接口定义如下:
/*
* 将 ArrayBuffer 数据转换为 base64。
*
* 参数 data - 原始数据
* 返回值 string - base64 数据
*/
function encode_Uint8Array(data: ArrayBuffer): string
/*
* 将 base64 数据转换为 ArrayBuffer
*
* 参数 data - base64 数据
* 返回值 ArrayBuffer - 原始数据
*/
function decode_Uint8Array(data: ArrayBuffer): ArrayBuffer
/*
* 将 string 类型数据转换为 base64。
*
* 参数 data - 原始数据
* 返回值 string - base64 数据
*/
function encode_String(data: string): string
/*
* 将 base64 数据转换为 string 数据。
*
* 参数 data - base64 数据
* 返回值 string - 原始数据
*/
function decode_string(data: string): string
/*
* 将 base64 数据转换为 ArrayBuffer 数据。
*
* 参数 data - base64 数据
* 返回值 ArrayBuffer - 原始数据
*/
function decode_String_toUint8Array(data: string): ArrayBuffer
为便于快速查阅,汇总为下表:
| 函数名 | 输入 | 输出 | 典型用途 |
|---|---|---|---|
encode_String(data) |
string 原始数据 | base64 字符串 | 文本内容编码 |
decode_string(data) |
base64 字符串 | 原始字符串 | 文本内容解码 |
encode_Uint8Array(data) |
字节数据 | base64 字符串 | 二进制内容编码(如文件、图片字节) |
decode_Uint8Array(data) |
字节形式的 base64 数据 | 原始字节数据 | 二进制内容解码 |
decode_String_toUint8Array(data) |
base64 字符串 | 字节数据 | base64 字符串还原为二进制 |
关于接口签名的实现细节
对照源码可以发现一个值得注意的细节:README 的接口注释以 ArrayBuffer 描述字节类型,而实际导出函数的 ArkTS 签名中,入参与返回值均为 Uint8Array(见 library/src/main/ets/components/MainPage.ets)。例如:
export function encode_Uint8Array(data: Uint8Array): string {
return cjlib.toBase64StringUtf8Arkts(data)
}
export function decode_String_toUint8Array(data: string): Uint8Array {
return new Uint8Array(cjlib.fromBase64StringUtf8Arkts(data))
}
而底层 Cangjie 互操作声明(library/src/main/cangjie/ark_interop_api/ark_interop_api.d.ts)中,字节类型则以 ArrayBuffer 呈现:
export declare interface CustomLib {
toBase64StringArkts(data: string): string
fromBase64StringArkts(data: string): string
toBase64StringUtf8Arkts(data: ArrayBuffer): string
fromBase64StringUtf8Arkts(data: string): ArrayBuffer
fromBase64Uint8Array(data: ArrayBuffer): ArrayBuffer
}
这属于 ArkTS 侧包装层对字节形态的归一化处理:调用方统一使用 Uint8Array 视图操作数据,底层互操作层内部按 ArrayBuffer 传递。实际使用时,请以源码签名为准,传入 Uint8Array 即可(Uint8Array 本身是 ArrayBuffer 的视图,两者可互相转换)。
源码级实现原理:ArkTS 与 Cangjie 的互操作调用链
base64-cj 之所以能实现「ArkTS 调用 Cangjie 算法」,依赖的是 OpenHarmony 的 ArkTS-Cangjie 互操作机制。整条调用链可以从三处源码还原:
1. ArkTS 侧:加载 Cangjie 动态库并导出
在 library/src/main/ets/components/MainPage.ets 中,通过互操作加载器动态加载 Cangjie 编译产物:
import { requireCJLib } from "libark_interop_loader.so";
import { CustomLib } from "libark_interop_api.so";
const cjlib = requireCJLib("libcangjie_base64.so") as CustomLib
其中:
libark_interop_loader.so提供requireCJLib(name)加载函数(类型声明见 library/src/main/cangjie/loader/libark_interop_loader.d.ts);libark_interop_api.so提供互操作接口声明CustomLib(见 library/src/main/cangjie/ark_interop_api/ark_interop_api.d.ts);- 实际加载的 Cangjie 动态库名为
libcangjie_base64.so,与 Cangjie 工程的包名cangjie_base64、输出类型dynamic一一对应(见 library/src/main/cangjie/cjpm.toml 的[package]配置)。
2. Cangjie 侧:@Interop[ArkTS] 标注导出函数
核心算法位于 library/src/main/cangjie/index.cj。该文件通过 @Interop[ArkTS] 宏将 Cangjie 函数暴露给 ArkTS,并直接复用 Cangjie 标准库 encoding.base64:
import ohos.ark_interop.*
import ohos.ark_interop_macro.*
import ohos.ark_interop_helper.*
import encoding.base64.*
@Interop[ArkTS]
public func toBase64StringArkts(data: String): String {
return toBase64String(data.toArray())
}
@Interop[ArkTS]
public func fromBase64StringArkts(data: String): String {
var opArr: Option<Array<Byte>> = fromBase64String(data)
var result = match (opArr) {
case Some(s) => unsafe { String.fromUtf8Unchecked(s)}
case None => ''
}
return result
}
@Interop[ArkTS]
public func toBase64StringUtf8Arkts(data: Array<Byte>): String {
return toBase64String(data)
}
@Interop[ArkTS]
public func fromBase64StringUtf8Arkts(data: String): Array<Byte> {
var opArr: Option<Array<Byte>> = fromBase64String(data)
var result: Array<Byte> = match (opArr) {
case Some(s) => s
case None => Array<Byte>()
}
return result
}
@Interop[ArkTS]
public func fromBase64Uint8Array(data: Array<Byte>): Array<Byte> {
var opArr: Option<Array<Byte>> = fromBase64String(String.fromUtf8(data))
var result: Array<Byte> = match (opArr) {
case Some(s) => s
case None => Array<Byte>()
}
return result
}
从中可以提取几个实现事实:
- 算法来源:
encoding.base64是 Cangjie 标准库模块,toBase64String负责编码、fromBase64String负责解码,因此算法正确性与性能由 Cangjie 运行时保证; - 解码容错设计:
fromBase64String返回Option<Array<Byte>>,Cangjie 侧通过match模式匹配处理:Some(s)时正常返回解码结果,None时返回空字符串或空数组,避免非法 Base64 输入直接抛错; - 编码路径差异:
toBase64StringArkts先将String转为字节数组(data.toArray())再编码,toBase64StringUtf8Arkts则直接对Array<Byte>编码,对应 ArkTS 侧的两个编码函数; - 解码字节的转码细节:
fromBase64StringUtf8Arkts解码后直接返回Array<Byte>;fromBase64Uint8Array则先把输入字节按 UTF-8 转成字符串再解码,对应 ArkTS 侧decode_Uint8Array的实现。
3. 构建侧:cjpm 目标与依赖配置
library/src/main/cangjie/cjpm.toml 是 Cangjie 工程的构建清单,关键配置包括:
- 包定义:
name = "cangjie_base64"、output-type = "dynamic"(生成动态库供加载器按名加载)、version = "1.0.0"、cjc-version = "0.48.2"; - 多目标平台:覆盖
aarch64-linux-ohos、x86_64-linux-ohos、x86_64-unknown-windows-gnu、aarch64-apple-darwin等目标,其中 OHOS 目标通过compile-option指定 LLVM、musl、openssl 的链接路径; - 库依赖:依赖本地资源库
cj_res(版本 1.0.0)。
此外,library/oh-package.json5 表明 HAR 包还依赖了三个互操作相关组件:
{
"name": "@cangjie-tpc/base64",
"version": "1.0.0",
"description": "ArkTS调用Cangjie版base64编解码算法实现。",
"main": "Index.ets",
"author": "cangjie-tpc",
"license": "Apache-2.0",
"repository": "https://gitcode.com/Cangjie-TPC/base64-cj.git",
"dependencies": {
"libark_interop_loader.so": "file:./src/main/cangjie/loader",
"cjhyapiregister": "file:./har/CJHyAPIRegister-v1.0.1.har",
"libark_interop_api.so": "file:./src/main/cangjie/ark_interop_api"
}
}
其中 cjhyapiregister 是位于 library/har/CJHyAPIRegister-v1.0.1.har 的 HAR 组件,负责 Cangjie API 的注册支撑。这些依赖在使用本地 module 方式引入时会随包一并解析。
安装与引入方式
README 提供了两种接入方式,开发者可根据工程情况任选其一。
方式一:通过中心仓下载安装
在应用工程的 oh-package.json5 依赖已配好的前提下,执行:
ohpm install @cangjie-tpc/base64
安装完成后,即可在代码中按模块名引入(引入方式见下文)。
方式二:通过本地 module 引入
适合需要直接调试库源码或不便访问中心仓的场景,步骤如下:
-
克隆下载项目(仓库地址为
https://gitcode.com/Cangjie-TPC/base64-cj.git,可参考 library/oh-package.json5 中repository字段); -
将 library 模块拷贝到应用项目下(即把仓库中的
library目录整体复制到目标工程); -
修改应用 entry 下的 oh-package.json5,在
dependencies字段添加本地依赖:{ "dependencies": { "@cangjie-tpc/base64": "file:../library" } }这与仓库示例工程自身的配置一致——entry/oh-package.json5 中正是以
"@cangjie-tpc/base64": "file:../library"的方式引用了 library 模块。 -
在项目中使用以下方式引入:
import { encode_String, decode_string, encode_Uint8Array, decode_Uint8Array, decode_String_toUint8Array } from "@cangjie-tpc/base64cj"
引入时的包名注意事项
README 示例代码中的导入路径写的是 @cangjie-tpc/base64cj,而依据 library/oh-package.json5 与 entry/oh-package.json5 的实际配置,仓库内真实有效的包名是 @cangjie-tpc/base64(ohpm install @cangjie-tpc/base64 亦与之对应)。实际集成时请统一使用:
import { encode_String, decode_string, encode_Uint8Array, decode_Uint8Array, decode_String_toUint8Array } from "@cangjie-tpc/base64"
仓库内的示例页面也均按此导入,例如 entry/src/main/ets/pages/CangjieBase64Example.ets 中的 import { encode_String, decode_string } from "@cangjie-tpc/base64"。
功能示例
示例一:字符串编解码(encode_String / decode_string)
README 给出的完整 ArkTS 页面示例,演示了字符串到 Base64、Base64 到字符串的往返过程,并配合 hilog 输出调试日志:
import { encode_String, decode_string } from "@cangjie-tpc/base64"
@Entry
@Component
struct CangjieBase64Example {
private data: string = "hello cangjie"
@State encodeResult: string = ''
@State decodeResult: string = ''
build() {
Row() {
Column() {
Button("encode")
.width(200)
.onClick((event: ClickEvent) => {
hilog.error(0x0000, 'CangjieBase64String', '%{public}s', `原数据: ${this.data}`);
this.encodeResult = encode_String(this.data)
hilog.error(0x0000, 'CangjieBase64String', '%{public}s', `encode: ${this.encodeResult}`);
}).margin(10)
Button("decode")
.width(200)
.onClick((event: ClickEvent) => {
hilog.error(0x0000, 'CangjieBase64String', '%{public}s', `原数据: ${this.encodeResult}`);
this.decodeResult = decode_string(this.encodeResult)
hilog.error(0x0000, 'CangjieBase64String', '%{public}s', `decode: ${this.decodeResult}`);
}).margin(10)
}
.width('100%')
}
.height('100%')
}
}
要点说明:
- 点击 encode 按钮:将
hello cangjie编码为 Base64 字符串并存入encodeResult; - 点击 decode 按钮:将
encodeResult解码还原为原始字符串并存入decodeResult; - 每次操作均通过
hilog.error输出原数据、编码结果、解码结果,便于在日志中验证往返一致性。
仓库中该示例的增强版位于 entry/src/main/ets/pages/CangjieBase64Example.ets,额外加入了循环次数(10 次 / 100 次)基准测试,用 systemDateTime.getTime(true) 计时并输出 ns/op 单次耗时,可帮助评估编解码性能。
示例二:字节数据编解码(encode_Uint8Array / decode_String_toUint8Array)
README 给出的字节形态示例,演示了如何将 UTF-8 字符串转换为 ArrayBuffer 后编码,以及将 Base64 字符串解码回字节数据:
import { encode_Uint8Array, decode_String_toUint8Array } from "@cangjie-tpc/base64"
@Entry
@Component
struct CangjieBase64Example {
private data: ArrayBuffer = Unit8ArrayUtils.StringToArrayBuffer("hello cangjie")
@State encodeResult: string = ''
build() {
Row() {
Column() {
Button("encode")
.width(200)
.onClick((event: ClickEvent) => {
this.encodeResult = encode_Uint8Array(this.data)
}).margin(10)
Button("decode")
.width(200)
.onClick((event: ClickEvent) => {
const decodeResult = decode_String_toUint8Array(this.encodeResult)
}).margin(10)
}
.width('100%')
}
.height('100%')
}
}
这里用到的 Unit8ArrayUtils.StringToArrayBuffer 是仓库提供的工具类方法(见 entry/src/main/ets/pages/common/Unit8ArrayUtils.ets),内部基于 @kit.ArkTS 的 util.TextEncoder 将字符串编码为 ArrayBuffer。同文件中还提供了:
Uint8ArrayToString/stringToUint8Array:Uint8Array 与字符串互转(手写 UTF-8 编解码逻辑);generateRandomArray(length):生成随机字节数组,便于构造测试数据;generateRandomString(length):基于随机字节数组生成随机字符串,可用于批量压测数据构造;StringToArrayBuffer(str):字符串转ArrayBuffer。
仓库中字节形态的完整增强示例位于 entry/src/main/ets/pages/CangjieBase64Utf8Example.ets,同样内置了 10 / 100 / 1000 / 10000 次循环的耗时基准测试,并额外演示了 decode_Uint8Array 的直接字节解码路径:
this.decodeResult = decode_Uint8Array(Unit8ArrayUtils.stringToUint8Array(this.encodeResult))
示例页面导航
示例应用 entry/src/main/ets/pages/Index.ets 提供了 5 个可点击跳转的入口,除上述两个 Cangjie 示例页外,还包含与 OpenHarmony 系统 Base64(@ohos.base64,见 entry/src/main/ets/pages/OhosBase64Example.ets)以及纯 JS 实现的 btoa/atob(见 entry/src/main/ets/pages/common/base64.js)的对照示例页,便于在真实设备上对比不同实现的编解码行为。
约束与限制
README 明确说明,该库在以下版本组合下验证通过:
DevEco Studio 5.0.13.210
Cangjie Support Plugin 5.0.13.210
结合 library/src/main/cangjie/cjpm.toml 的配置可以推断,构建时还需要满足:
- Cangjie 编译器版本与
cjc-version = "0.48.2"匹配(环境变量DEVECO_CANGJIE_HOME需指向 Cangjie 工具链目录,供编译选项中的 LLVM、musl、openssl 路径解析使用); - 目标设备架构为 OpenHarmony 支持的
aarch64-linux-ohos(真机/模拟器常见架构)或x86_64-linux-ohos(模拟器)。
因此,集成前请确认开发环境版本与目标设备架构满足上述要求;在其他版本组合下的兼容性,需要开发者自行验证。
开源协议与参与贡献
本项目基于 Apache License 2.0 开源(许可证全文见 library/LICENSE),可自由使用、修改与参与社区共建。欢迎通过提交 PR、提交 Issue 或任何形式参与贡献,贡献入口与维护说明以 library/README.md 为准。