本文结合当前仓库 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 重写一遍,而是:

  1. 把原先 add_executable 的主程序,在 OHOS 交叉编译条件下改为 add_library(... SHARED ...),产出与 ArkTS 侧 import ... from 'libentry.so' 一致的原生库;
  2. 把 FileZilla 风格的"会话管理 / 上传下载 / 队列 / 断点续传 / 编码识别 / 远程文件操作"这些核心能力,通过 NAPI 暴露给上层 ArkTS;
  3. 把传输过程中的 进度回调 / 错误码 / 控制命令应答 经由 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 实现。

参考链接:


三、本仓库中的代码布局

鸿蒙工程根目录: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_progressnapi_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.sonm_modnameimport 路径 三方必须严格对齐,否则运行时会抛 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.alibuchardet.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

这样做的好处:

  1. 每个业务子模块都是独立的静态库,便于单元测试或抽出复用;
  2. 顶层只做 NAPI 桥接和最终链接,避免上千行 CMake;
  3. 增加新协议(如 WebDAV、S3)时,只需新增子目录 + add_subdirectory() + target_link_libraries(entry ... newProto),不必改动其它子模块。

Task 4:NAPI 桥接 FileZilla 等价能力

将"上传 / 下载 / 控制命令(CWD / MKD / RMD / RNFR-RNTO / DELE)/ 调试命令 / 任务取消"等核心能力,按"一个原生函数对应一个 ArkTS 可调用方法"的方式注册到 napi_property_descriptor 中(见上节节选)。

每个长任务(上传 / 下载)必须遵循的"三件套":

  1. napi_create_threadsafe_function:把 ArkTS 传入的 onMessage / onProgress 回调包裹为线程安全函数;
  2. 独立工作线程:在工作线程中跑 curl_easy_perform / libssh2_sftp_*,避免阻塞 UI 线程;
  3. 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.json5deviceTypes 已包含 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.json5externalNativeOptions.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.json5externalNativeOptions.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.json5requestPermissions 至少包含:

权限 用途
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.json5deviceTypes 配置为 ["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.json5abiFilters
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 等价能力。


参考链接

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐