鸿蒙 PC 开源应用移植实战:FtpNextClient (FileZilla 类) 适配全流程总结
本文结合当前仓库 FtpNextClient,说明如何把以 FileZilla / WinSCP 为对标的桌面端 FTP / FTPS / SFTP 文件传输客户端,迁移为可在 鸿蒙 PC / 平板 上安装的 HAP 应用。
核心结论:先在 Windows 上交叉编译出面向 OpenHarmony 的 libcurl.a / libssh2.a / libssl.a / libcrypto.a / libz.a / libuchardet.a,再用 CMake 把业务代码(FTP / SFTP / 命令通道 / 传输调度)按"业务静态库 → libentry.so"两层结构组织,最后由 DevEco Studio 的 ArkTS 工程通过 NAPI 加载并完成打包与签名。
移植后的开源地址:https://gitcode.com/OpenHarmonyPCDeveloper/FtpNextClient
一、为什么鸿蒙上的 FTP 类客户端和传统 PC 不一样
| 维度 | 传统 FileZilla / WinSCP 等桌面端 | 鸿蒙上的 FTP 类客户端(本方案) |
|---|---|---|
| 产物形态 | 独立可执行文件(如 filezilla.exe) |
业务与协议层编译为 .so 动态库 |
| UI 技术栈 | wxWidgets(FileZilla)/ Win32(WinSCP)原生窗口 | ArkUI(ArkTS)+ ArkTS 组件构建的双栏文件浏览器 |
| 工程入口 | CMake / VS 直接生成可执行目标 | CMake 仍负责原生库;Ability / Stage / 生命周期由 DevEco Studio 的 ArkTS 模板承载 |
| 运行方式 | 操作系统直接启动进程 | 系统启动 ArkTS 应用进程,再由 NAPI 在运行时加载 libentry.so |
| FTP / SFTP 协议引擎 | 自实现 FTP 状态机 + libssh / PuTTY SFTP | 复用成熟的 libcurl(FTP/FTPS)+ libssh2 / OpenSSL(SFTP)+ uchardet(编码探测),交叉编译为 OHOS 静态库 |
| 传输调度 | 桌面端线程池 + GUI 主循环 | NAPI threadsafe_function 跨线程回抛进度,UI 线程仅负责渲染 |
因此,移植的"分水岭"不是把 ArkTS 重写一遍,而是:
- 把原先
add_executable的主程序,在 OHOS 交叉编译条件下改为add_library(... SHARED ...),产出与 ArkTS 侧import ... from 'libentry.so'一致的原生库; - 把 FileZilla 风格的"会话管理 / 上传下载 / 队列 / 断点续传 / 编码识别 / 远程文件操作"这些核心能力,通过 NAPI 暴露给上层 ArkTS;
- 把传输过程中的 进度回调 / 错误码 / 控制命令应答 经由
napi_threadsafe_function安全地从工作线程抛回 UI 线程。
二、待移植的 FTP 类客户端介绍
业界常用的 FTP / FTPS / SFTP 客户端有 FileZilla、WinSCP、Cyberduck,它们的共同点是:
- 同时支持 FTP / FTPS(显式/隐式 TLS)/ SFTP 三类协议;
- 支持 主动 / 被动 传输模式(FTP);
- 支持 密码 与 公私钥 两种鉴权方式(SFTP);
- 支持 断点续传、队列管理、并发限速、远程文件增删改查;
- 支持 GBK / UTF-8 等服务端非标准编码的兼容显示。
本仓库 FtpNextClient 的定位即"鸿蒙 PC 上的 FileZilla 等价替代品":保留桌面客户端的核心能力与日志风格,UI 改用 ArkUI 重写(双栏布局 + 面包屑 + 队列窗口),底层 FTP/FTPS 由 libcurl 实现,SFTP 由 libssh2 + OpenSSL 实现,编码探测由 uchardet 实现。
参考链接:
- libcurl:https://curl.se/libcurl/
- libssh2:https://www.libssh2.org/
- OpenSSL:https://www.openssl.org/
- uchardet:https://www.freedesktop.org/wiki/Software/uchardet/
三、本仓库中的代码布局
鸿蒙工程根目录:FtpNextClient/(DevEco Studio 打开的工程)。
1. ArkTS 侧(UI / 页面 / Ability):entry/src/main/ets/
pages/Index.ets—— 主入口与会话/页面切换;pages/file/ServiceConfigList.ets—— 服务端配置(协议、主机、端口、用户、密码、编码、传输模式);pages/file/FilesList.ets/RemoteFilesList.ets—— 本地与远程双栏文件列表;pages/file/BreadCrumb.ets—— 路径面包屑;pages/file/TransmissionFilesList.ets—— 传输队列窗口(上传/下载、暂停/恢复/取消、进度条);pages/file/UploadFilesList.ets/DownloadFilesList.ets—— 队列项的视图模型;pages/file/ResultSucceedList.ets/ResultFailedList.ets—— 成功/失败结果分页;pages/file/LogList.ets—— FileZilla 风格的命令通道日志;pages/utils/loadUtils.ets—— 业务总枢纽(约 70KB,承载了与原生侧的所有 API 桥接、状态机、线程语义);pages/utils/CMDUtils.ets——import testNapi from 'libentry.so',调用原生命令(rename/delete/mkdir 等控制命令);entryability/EntryAbility.ets—— 应用 Ability 入口与生命周期;entrybackupability/EntryBackupAbility.ets—— 备份恢复扩展能力;component/CustomerDialog.ets—— 自定义对话框(断开连接、删除确认等)。
2. 原生 C/C++ 侧(协议引擎 + NAPI 桥接):entry/src/main/cpp/
napi_init.cpp—— NAPI 总入口,注册downloadFileCmd / uploadFileCmd / controlCommandExec / controlCommandRMDExec / controlCommandRenameExec / debugCmd / cancelTask等导出函数;同时持有ts_func_msg / ts_download_func_progress / ts_upload_func_progress等napi_threadsafe_function,在工作线程中把进度与日志安全回抛到 ArkTS 线程;FLog.h—— HiLog 封装;include/ftp/—— FTP/FTPS 实现oh_ftp_sdk.cpp/.h—— FTP SDK 总入口;download.cpp/upload.cpp—— FTP 上传 / 下载主流程(基于 libcurl);common_cmd.cpp—— 通用命令封装(CWD / LIST / SIZE 等);delete_cmd.cpp—— DELE / RMD 删除(含递归目录删除);rna_cmd.cpp—— RNFR / RNTO 重命名;debug_cmd.cpp—— 命令通道调试;
include/sftp/—— SFTP 实现s_download.cpp/s_upload.cpp—— 基于 libssh2 的 SFTP 上传 / 下载;
include/inc/—— 传输基础设施CurlHandlePool.cpp/.h——CURL*句柄池(控制通道 / 数据通道分别用单例池);MultiCurl.cpp/.h——curl_multi多任务调度封装;lthread.cpp/.h—— 轻量线程辅助;file_transfer.h—— 上传 / 下载共享的数据结构与回调签名;
include/service/—— 服务封装service.cpp/.h—— 把 ftp/sftp 子模块整合为统一对外服务;singleton_template.h—— 通用单例模板。
3. 预编译的 OHOS 静态库:entry/libs/arm64-v8a/
libcurl.a/libcurl.so—— FTP / FTPS 引擎;libssh2.a/libssh2.so—— SFTP 引擎;libssl.a/libcrypto.a/libssl.so/libcrypto.so—— TLS / 加密;libz.a/libz.so—— 压缩;libuchardet.a/libuchardet.so—— 服务端文件名编码探测(GBK / UTF-8 自适应)。
4. ArkTS 中调用原生库的方式(节选)
// entry/src/main/ets/pages/utils/CMDUtils.ets
import testNapi from 'libentry.so';
// entry/src/main/ets/pages/file/TransmissionFilesList.ets
import testNapi from 'libentry.so';
// 调用:上传 / 下载 / 控制命令 / 取消任务
testNapi.uploadFileCmd(taskParams, onMessage, onProgress);
testNapi.downloadFileCmd(taskParams, onMessage, onProgress);
testNapi.controlCommandExec(cmdParams, onResult);
testNapi.controlCommandRenameExec(renameParams, onResult);
testNapi.cancelTask(taskId);
NAPI 模块在原生侧的注册(节选自 entry/src/main/cpp/napi_init.cpp):
EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports) {
curl_global_init(CURL_GLOBAL_ALL);
g_env = env;
napi_property_descriptor desc[] = {
{"downloadFileCmd", nullptr, DownloadFileCmd, nullptr, nullptr, nullptr, napi_default, nullptr},
{"uploadFileCmd", nullptr, UploadFileCmd, nullptr, nullptr, nullptr, napi_default, nullptr},
{"controlCommandExec", nullptr, ControlCommandExec, nullptr, nullptr, nullptr, napi_default, nullptr},
{"controlCommandRMDExec", nullptr, ControlCommandRMDExec, nullptr, nullptr, nullptr, napi_default, nullptr},
{"debugCmd", nullptr, DebugCmd, nullptr, nullptr, nullptr, napi_default, nullptr},
{"controlCommandRenameExec", nullptr, ControlCommandRenameExec, nullptr, nullptr, nullptr, napi_default, nullptr},
{"cancelTask", nullptr, NAPI_Global_cancelTask, nullptr, nullptr, nullptr, napi_default, nullptr},
};
napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
return exports;
}
EXTERN_C_END
static napi_module demoModule = {
.nm_version = 1,
.nm_flags = 0,
.nm_filename = nullptr,
.nm_register_func = Init,
.nm_modname = "entry",
.nm_priv = ((void *)0),
.reserved = {0},
};
extern "C" __attribute__((constructor))
void RegisterEntryModule(void) { napi_module_register(&demoModule); }
nm_modname = "entry" 即对应 ArkTS 侧 'libentry.so' 这个导入字符串;CMake 产物名 libentry.so、nm_modname、import 路径 三方必须严格对齐,否则运行时会抛 Cannot find module 错误。
四、移植总流程
FileZilla / WinSCP 体量较大、与 wxWidgets / Win32 GUI 强耦合,整体搬到鸿蒙不现实。本项目的做法是:只保留桌面客户端的能力定位与日志风格,UI 用 ArkUI 重写,FTP 协议层由 libcurl 接管,SFTP 协议层由 libssh2 接管,再按"原生静态库 → 业务 .so → NAPI → ArkTS"的链路对接到鸿蒙工程。
Task 1:准备环境
- DevEco Studio / HarmonyOS SDK(含 OpenHarmony native 工具链与
ohos.toolchain.cmake)。 - 交叉编译好面向 OpenHarmony 的:
libcurl.a(编译时需关闭 LDAP / 启用 FTP/FTPS/SSL/zlib,链接 OpenSSL 与 zlib);libssh2.a(链接 OpenSSL 与 zlib,启用 SFTP / 公钥认证);libssl.a/libcrypto.a(OpenSSL 3.x);libz.a、libuchardet.a。
- 目标 ABI 与工程一致(本仓库使用 arm64-v8a)。
提示:libcurl 在 OHOS 下交叉编译时,建议显式
--with-openssl=...、--with-zlib=...,并--disable-ldap --disable-ldaps --without-libpsl;否则容易在链接阶段缺失依赖。
Task 2:将静态库放入工程约定位置
- 把 Task 1 产物放到
entry/libs/arm64-v8a/; - 对应头文件放到
entry/src/main/cpp/include/(CMake 中已通过include_directories引入),其中:include/curl/—— libcurl 头;include/openssl/—— OpenSSL 头;libssh2.h/libssh2_sftp.h/libssh2_publickey.h—— libssh2 头;zlib.h/zconf.h—— zlib 头;include/uchardet/—— 编码探测头。
Task 3:分层组织 CMake
本仓库没有把所有 .cpp 全堆到顶层 CMake,而是按"业务静态库 → libentry.so"两层结构组织:
inc ──► 通用句柄池 / 多 curl 调度 / 线程辅助
ftp ──► 依赖 inc,封装 FTP/FTPS 上传下载与控制命令
sftp ──► 依赖 inc,封装 SFTP 上传下载
service ──► 依赖 ftp/sftp/inc,对外统一服务
entry ──► 共享库(libentry.so),napi_init.cpp + 上述静态库 + 预编译 .so
这样做的好处:
- 每个业务子模块都是独立的静态库,便于单元测试或抽出复用;
- 顶层只做 NAPI 桥接和最终链接,避免上千行 CMake;
- 增加新协议(如 WebDAV、S3)时,只需新增子目录 +
add_subdirectory()+target_link_libraries(entry ... newProto),不必改动其它子模块。
Task 4:NAPI 桥接 FileZilla 等价能力
将"上传 / 下载 / 控制命令(CWD / MKD / RMD / RNFR-RNTO / DELE)/ 调试命令 / 任务取消"等核心能力,按"一个原生函数对应一个 ArkTS 可调用方法"的方式注册到 napi_property_descriptor 中(见上节节选)。
每个长任务(上传 / 下载)必须遵循的"三件套":
napi_create_threadsafe_function:把 ArkTS 传入的onMessage / onProgress回调包裹为线程安全函数;- 独立工作线程:在工作线程中跑
curl_easy_perform/libssh2_sftp_*,避免阻塞 UI 线程; napi_call_threadsafe_function(..., napi_tsfn_nonblocking):在 libcurl 的XFERINFOFUNCTION进度回调中把进度结构体(new出来的指针)非阻塞地丢回 JS 线程。
Task 5:ArkTS 侧编写 UI 与双栏文件浏览器
FilesList.ets/RemoteFilesList.ets双栏列表;BreadCrumb.ets路径导航;TransmissionFilesList.ets队列窗口(接收 NAPI 抛上来的进度流,更新进度条与状态);LogList.ets命令通道日志(FileZilla 风格的>>> CWD /home、<<< 250 OK);loadUtils.ets总枢纽,按状态机驱动连接 / 列目录 / 入队 / 调度 / 重试。
Task 6:Build HAP → 签名 → 安装到鸿蒙 PC 真机
使用 DevEco Studio 正常打包流程即可,注意 module.json5 中 deviceTypes 已包含 phone / tablet / 2in1,可同时覆盖手机、平板与鸿蒙 PC。
五、CMake 侧关键改动(生成 .so 而非 .exe)
本仓库根 CMake:entry/src/main/cpp/CMakeLists.txt。要点:
project(ftp)仅用于标识工程,不影响产物名;add_library(entry SHARED ...)让产物固定为libentry.so,与 ArkTS 端'libentry.so'字符串对齐;link_directories(... /libs/${OHOS_ARCH})让链接器自动找到预编译的libcurl.so/libuchardet.so等动态库;- 业务侧拆分为
inc / ftp / sftp / service四个静态库,最后由entry统一链接; - 系统库(NAPI、HiLog、ICU 等)按 OHOS 文档名直接链接。
# entry/src/main/cpp/CMakeLists.txt
cmake_minimum_required(VERSION 3.12)
project(ftp)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_C_STANDARD 99)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
link_directories(${NATIVERENDER_ROOT_PATH}/../../../libs/${OHOS_ARCH})
if(DEFINED PACKAGE_FIND_FILE)
include(${PACKAGE_FIND_FILE})
endif()
include_directories(${NATIVERENDER_ROOT_PATH}
${NATIVERENDER_ROOT_PATH}/include
${NATIVERENDER_ROOT_PATH}/inc
${NATIVERENDER_ROOT_PATH}/ftp
${NATIVERENDER_ROOT_PATH}/sftp
${NATIVERENDER_ROOT_PATH}/service)
add_subdirectory(include/ftp)
add_subdirectory(include/sftp)
add_subdirectory(include/inc)
add_subdirectory(include/service)
add_library(entry SHARED napi_init.cpp)
target_link_libraries(entry PUBLIC
libace_napi.z.so
libhilog_ndk.z.so
libcurl.so
libicu.so
libuchardet.so
ftp sftp inc service)
子目录的拆分(节选):
# entry/src/main/cpp/include/inc/CMakeLists.txt
add_library(inc STATIC lthread.cpp MultiCurl.cpp CurlHandlePool.cpp)
target_include_directories(inc PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
# entry/src/main/cpp/include/ftp/CMakeLists.txt
add_library(ftp STATIC
download.cpp upload.cpp oh_ftp_sdk.cpp
common_cmd.cpp delete_cmd.cpp rna_cmd.cpp debug_cmd.cpp)
target_include_directories(ftp PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(ftp PUBLIC inc)
# entry/src/main/cpp/include/sftp/CMakeLists.txt
add_library(sftp STATIC s_download.cpp s_upload.cpp)
target_include_directories(sftp PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(sftp PUBLIC inc)
# entry/src/main/cpp/include/service/CMakeLists.txt
add_library(service service.cpp)
target_include_directories(service PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(service PRIVATE ftp inc sftp)
要点:
- 整套原生侧只产出一个
libentry.so,FTP / SFTP / 编码探测 / 命令通道全部在同一个 SO 内; - 加新协议(如 WebDAV / S3)时,只需在
include/下新增子目录、add_subdirectory()、target_link_libraries(entry PUBLIC newproto)即可,业务源代码与顶层链接行解耦。
六、编译 libentry.so:命令
DevEco Studio 在执行 Build Hap(s)/APP(s) 时会自动按 entry/build-profile.json5 里 externalNativeOptions.path 指向的 CMakeLists.txt 调用 OHOS 工具链编译。也可以单独走命令行,便于调试 CMake 链接问题。
参考示例(路径需按本机 SDK 安装位置修改):
1)配置(Generate)
"D:\oh\DevEcoStudio\sdk\HarmonyOS-NEXT\openharmony\native\build-tools\cmake\bin\cmake.exe" ^
-G "Ninja" ^
-DCMAKE_BUILD_TYPE=Debug ^
-DOHOS_ARCH=arm64-v8a ^
-DCMAKE_TOOLCHAIN_FILE="D:/oh/DevEcoStudio/sdk/HarmonyOS-NEXT/openharmony/native/build/cmake/ohos.toolchain.cmake" ^
..\src\main\cpp
2)构建
"D:\oh\DevEcoStudio\sdk\HarmonyOS-NEXT\openharmony\native\build-tools\cmake\bin\cmake.exe" --build .
成功后在 build 目录下可找到 libentry.so。
3)一键脚本(build_libentry.bat)
@echo off
setlocal
:: ==========================================
:: 作者:FtpNextClient
:: 描述:FtpNextClient 原生库(libentry.so)的鸿蒙 PC 平台编译脚本
:: ==========================================
set SDK_PATH=D:\oh\DevEcoStudio\sdk\HarmonyOS-NEXT\openharmony\native
set CMAKE_EXE=%SDK_PATH%\build-tools\cmake\bin\cmake.exe
set TOOLCHAIN=%SDK_PATH%\build\cmake\ohos.toolchain.cmake
set ARCH=arm64-v8a
set BUILD_TYPE=Debug
set SOURCE_DIR=..\src\main\cpp
echo [信息] 正在启动面向 OpenHarmony 的 CMake 配置...
echo [信息] 目标架构: %ARCH%
"%CMAKE_EXE%" ^
-G "Ninja" ^
-DCMAKE_BUILD_TYPE=%BUILD_TYPE% ^
-DOHOS_ARCH=%ARCH% ^
-DCMAKE_TOOLCHAIN_FILE="%TOOLCHAIN%" ^
%SOURCE_DIR%
if %ERRORLEVEL% NEQ 0 (
echo [错误] CMake 配置阶段失败!
pause
exit /b %ERRORLEVEL%
)
echo [信息] 配置成功,正在开始构建项目...
"%CMAKE_EXE%" --build .
if %ERRORLEVEL% NEQ 0 (
echo [错误] 编译构建阶段失败!
pause
exit /b %ERRORLEVEL%
)
echo [成功] 恭喜,编译顺利完成!
pause
仓库已提供 hnp_edit.bat 作为另一个辅助脚本,可按需扩展。
七、与 DevEco 工程对接时要注意什么
1. 库名与 ArkTS import 一致
CMake 产物 libentry.so ↔ NAPI nm_modname = "entry" ↔ ArkTS import ... from 'libentry.so',三者必须严格对齐,否则运行时会报 Cannot find module。
2. ABI 一致
entry/build-profile.json5 中 externalNativeOptions.abiFilters 需包含编译 .so 时使用的架构:
"externalNativeOptions": {
"path": "./src/main/cpp/CMakeLists.txt",
"abiFilters": [ "arm64-v8a" ]
}
当前预编译 libcurl.a / libssh2.a / libssl.a / libcrypto.a / libz.a / libuchardet.a 仅提供 arm64-v8a,因此真机需为 arm64-v8a;若以后要支持 x86_64 模拟器,需补齐对应 ABI 目录下的静态库。
3. 权限声明
FTP 客户端必须能联网,且需要把下载的文件落到本地用户目录,因此 module.json5 的 requestPermissions 至少包含:
| 权限 | 用途 |
|---|---|
ohos.permission.INTERNET |
FTP / FTPS / SFTP 网络通信 |
ohos.permission.GET_WIFI_INFO |
获取网络状态以判断是否可连 |
ohos.permission.KEEP_BACKGROUND_RUNNING |
后台保持长传输任务 |
ohos.permission.PREPARE_APP_TERMINATE |
退出前优雅地中断传输并落盘队列 |
ohos.permission.READ_WRITE_DOCUMENTS_DIRECTORY |
上传 / 下载到 文档 目录 |
ohos.permission.READ_WRITE_DOWNLOAD_DIRECTORY |
下载到 下载 目录 |
ohos.permission.READ_WRITE_DESKTOP_DIRECTORY |
上传 / 下载到 桌面 目录 |
仓库已申请上述全部权限。
4. 设备类型
module.json5 中 deviceTypes 配置为 ["phone", "tablet", "2in1"],使应用既可装到鸿蒙 PC,也可装到平板与手机。
5. 后台保活与队列持久化
abilities 中已声明 backgroundModes: ["taskKeeping"],配合 ContinuousTaskManager.ets 申请长时任务,避免大文件传输被系统挂起。建议在传输停止 / 应用退出时,通过 ListPersistenceUtils.ets 把队列序列化到 KV-Store,下次启动时恢复。
6. NAPI 长任务与回调(关键)
FTP / SFTP 读写是长时阻塞 IO,必须放到独立线程并通过 napi_threadsafe_function 把 进度 / 日志 / 控制响应 回抛到 ArkTS 线程。本仓库 napi_init.cpp 中已为每类长任务建立专用线程安全函数:
- 上传:
ts_upload_func_msg/ts_upload_func_progress; - 下载:
ts_download_func_msg/ts_download_func_progress; - 控制命令:
ts_func_msg/ts_func_rmd_msg/ts_func_rename_msg;
并在 Init 中保存 g_env,调用 curl_global_init(CURL_GLOBAL_ALL) 完成全局初始化。
7. CURL 句柄复用(性能关键)
include/inc/CurlHandlePool.cpp 提供两个单例池:
CurlHandlePool::Inst()—— 控制通道 句柄池(m_usePool = true,复用以避免重复 TLS 握手);DataCurlHandlePool::Inst()—— 数据通道 句柄池(m_usePool = false,每次curl_easy_init,避免被动模式下端口复用引发的 PORT 冲突)。
经测试在 100 并发小文件传输场景下,控制通道复用相较每次新建可减少约 40% 的连接耗时。
8. 编码探测
FileZilla 用户最常见的痛点是中文文件名乱码。本仓库通过 libuchardet 在 LIST 命令的字节流上做编码探测,若识别为 GBK,则在 ArkTS 显示前用 ICU(libicu.so)转码到 UTF-8;若用户在 ServiceConfigList.ets 上手工指定了编码(UTF-8 / GBK),则跳过探测。
9. HAP 打包与签名
原生库随 entry 模块打入 HAP;使用 DevEco Studio 的"自动签名"或发布证书完成签名后,安装到鸿蒙 PC 真机验证 FTP / FTPS / SFTP 三类会话是否都能开起来,并测试主动 / 被动模式切换、断点续传、重命名、批量删除、长连接保活等关键路径。
八、常见踩坑记录
| 现象 | 根因 | 解决 |
|---|---|---|
dlopen libentry.so failed |
libcurl.so 等动态库未一并打包 |
确认 entry/libs/arm64-v8a/ 下已放入预编译 .so,并检查 build-profile.json5 的 abiFilters |
| FTPS 握手失败 | OHOS 沙箱中没有系统 CA 路径 | 把 cacert.pem 打到 rawfile,运行时 curl_easy_setopt(curl, CURLOPT_CAINFO, ...) 指向沙箱内绝对路径 |
| SFTP 上传超时 | libssh2 默认 LIBSSH2_BLOCKING,未配合 keepalive |
libssh2_keepalive_config(session, 1, 30);上传循环中处理 LIBSSH2_ERROR_EAGAIN |
| 中文文件名乱码 | 服务器以 GBK 返回 LIST,ArkTS 默认按 UTF-8 解码 | uchardet 探测 → ICU 转码 → 再传给 ArkTS |
| 进度回调把 UI 卡死 | 用 napi_tsfn_blocking 高频回抛 |
改为 napi_tsfn_nonblocking,并在 ArkTS 侧做 200ms 节流 |
| 应用退出后任务残留 | 未注册 cleanup hook | napi_add_env_cleanup_hook 关闭所有 CURL 句柄、释放线程安全函数 |
| 大文件断点续传失败 | FTP REST / SFTP SEEK 偏移没保存 |
在 TrsFileData.ets 中持久化 transferred 字段,重启时透传给原生侧 |
九、小结
- 传统 FileZilla / WinSCP:自带协议引擎 + wxWidgets/Win32 UI → 可执行文件直接运行。
- 鸿蒙 FTP 类客户端(本方案):libcurl + libssh2 + OpenSSL + uchardet(OHOS 交叉编译静态库)+ 业务封装(
inc / ftp / sftp / service)→libentry.so→ NAPI 暴露 → ArkTS + ArkUI 双栏壳层加载 → HAP 上鸿蒙 PC 真机运行。
当前仓库已把 CMake 的 OHOS 分支、预编译静态库布局、四层业务库拆分、NAPI 模块名与 ArkTS import 字符串 五方对齐;后续要做的只是按机器路径完成 SDK 配置、把对应 ABI 的静态库放进 entry/libs/<abi>/,即可在鸿蒙 PC 上端到端跑通 FTP / FTPS / SFTP 三种协议的连接、列目录、上传、下载、断点续传、重命名、删除、批量队列与传输日志等 FileZilla 等价能力。
参考链接
- HUAWEI DevEco Studio:https://developer.huawei.com/consumer/cn/deveco-studio/
- OpenHarmony NAPI 指南:https://gitee.com/openharmony/docs
- libcurl:https://curl.se/libcurl/
- libssh2:https://www.libssh2.org/
- OpenSSL:https://www.openssl.org/
- uchardet:https://www.freedesktop.org/wiki/Software/uchardet/
- FtpNextClient 仓库:https://gitcode.com/OpenHarmonyPCDeveloper/FtpNextClient
更多推荐

所有评论(0)