news 2026/9/16 20:48:57

Momentum-Firmware 的 JS 文件选择器:gui/file_picker 模块与 pickFile 实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Momentum-Firmware 的 JS 文件选择器:gui/file_picker 模块与 pickFile 实战指南

Momentum-Firmware 的 JS 文件选择器:gui/file_picker 模块与 pickFile 实战指南

【免费下载链接】Momentum-Firmware🐬 Feature-rich, stable and customizable Flipper Firmware项目地址: https://gitcode.com/GitHub_Trending/mo/Momentum-Firmware

导读

在 Flipper 固件的 JS 脚本生态中,让用户从设备存储中选择一个文件是许多脚本(如解析协议文件、读取配置、选择资产)的刚需。Momentum-Firmware 通过gui/file_picker模块提供pickFile()函数,以极简的参数设计(起始路径 + 扩展名过滤)弹出一个原生文件浏览对话框,并返回所选文件的绝对路径。读完本文,你将掌握该 API 的完整签名、扩展名过滤规则、取消分支处理,以及它背后从 JS 模块到dialogs系统服务的完整调用链。

一、模块定位:一个 Prompt 函数,而非 GUI 视图

js_gui__file_picker在文档中被明确描述为:

Allows asking the user to select a file. It is not GUI view like other JS GUI views, rather just a function that shows a prompt.

这意味着它与其他 JS GUI 模块(如gui/dialoggui/submenugui/text_input等"视图")有本质区别:

  • 视图模块通常需要与viewDispatcher、事件循环event_loop配合,先创建视图、注册回调、切换显示,再异步等待用户交互事件;
  • 文件选择器则是一个同步阻塞式函数调用:调用即弹出对话框,用户完成选择或取消后函数直接返回结果,脚本继续向下执行。

这种设计让文件选择成为脚本里"即用即走"的一个步骤,非常适合需要先选定文件再做后续处理的线性流程脚本。

从源码结构看,该模块注册的模块名是gui__file_picker(内部 ID),在 JS 侧通过require("gui/file_picker")导入。相关文件如下:

  • JS 模块实现:applications/system/js_app/modules/js_gui/file_picker.c
  • TypeScript 类型声明:applications/system/js_app/packages/fz-sdk/gui/file_picker.d.ts
  • 官方示例脚本:applications/system/js_app/examples/apps/Scripts/Examples/gui.js

二、API 参考:pickFile() 完整说明

pickFile()gui/file_picker模块导出的唯一函数,对应底层 C 实现js_gui_file_picker_pick_file

函数签名

// 来自 file_picker.d.ts(JS SDK 0.1 引入) declare function pickFile(basePath: string, extension: string): string | undefined;

参数详解

参数类型必填说明
basePathstring文件浏览器启动时所在的起始目录路径
extensionstring要展示的文件扩展名过滤规则

返回值

  • string:用户选中文件后,返回该文件的绝对路径字符串(例如/ext/subghz/Test.sub);
  • undefined:用户按下返回键取消选择时返回,脚本中需用if (path)之类的方式做判空处理。

参数校验细节(源码层面)

C 层实现使用JsValueDeclaration声明了两个参数均为字符串类型,并通过JS_VALUE_PARSE_ARGS_OR_RETURN解析(见 file_picker.c)。也就是说:

  • 两个参数都必须提供且为字符串,否则解析失败会直接返回(不弹窗);
  • 这是 JS SDK 0.1 起就稳定提供的 API,类型声明见 file_picker.d.ts。

三、extension 过滤规则的三种用法

extension参数用于控制文件浏览器中可见的文件类型,文档给出了三种典型写法:

写法含义示例场景
".sub"仅显示该单一扩展名的文件让用户挑选 Sub-GHz 信号文件
".iso|.img"|分隔多个扩展名,显示其中任意一种选择镜像文件(磁盘映像)
"*"通配符,显示所有文件不限制类型的通用选择

官方示例 gui.js 中使用的是"*",从/ext(SD 卡根目录)开始让用户自由选择任何文件:

let path = filePicker.pickFile("/ext", "*");

注意extension的匹配只作用于文件,不作用于目录——目录始终可见,方便用户逐层导航进入子目录。这是文件浏览器对话框的通用行为(DialogsFileBrowserOptions中的extension仅用于过滤可被选中的文件)。

四、完整实战示例:选择文件后展示结果

结合官方示例脚本 gui.js,一个完整的文件选择流程如下:

// 1. 导入所需模块 let gui = require("gui"); let filePicker = require("gui/file_picker"); let dialogView = require("gui/dialog"); // 2. 弹出文件选择器,从 SD 卡根目录开始、不限制扩展名 let path = filePicker.pickFile("/ext", "*"); // 3. 处理返回结果 if (path) { // 用户选中了文件:显示完整路径 views.helloDialog.set("text", "You selected:\n" + path); } else { // 用户按返回取消:给用户友好提示 views.helloDialog.set("text", "You didn't select a file"); } // 4. 切换到对话框视图展示结果 gui.viewDispatcher.switchTo(views.helloDialog);

实战要点:

  • 取消是常见操作,必须处理undefined。Flipper 用户习惯按 Back 键退出弹窗,若不判空直接使用返回值,后续字符串拼接或存储操作会出错;
  • basePath常用/ext(SD 卡),但也可指向任意已挂载路径,例如/any/int(内部存储)等,取决于业务需要;
  • 该调用是阻塞式的:弹出对话框期间脚本暂停,用户操作完成后才继续执行,因此适合放在事件回调或普通流程代码中,而不需要像视图那样手动订阅事件。

五、源码级原理:pickFile 的底层调用链

要深入理解pickFile的行为,可以顺着源码追踪其完整调用链:

1. JS 模块入口(C 层)

file_picker.c 中js_gui_file_picker_pick_file的实现逻辑非常直接:

  1. 解析两个字符串参数base_pathextension
  2. 通过furi_record_open(RECORD_DIALOGS)打开系统Dialogs 服务
  3. 构造DialogsFileBrowserOptions配置(设置.extension.icon = &I_file_10px.base_path);
  4. 调用dialog_file_browser_show(dialogs, path, path, &browser_options)同步弹出文件浏览器;
  5. 若返回true(选中),用mjs_mk_string把路径字符串返回给 JS 层;否则返回MJS_UNDEFINED(对应 JS 的undefined);
  6. 释放 FuriString 并关闭 Dialogs 服务记录。

值得注意的实现细节:模块把文件图标固定为I_file_10px(系统内置的通用文件图标),同时把base_path既作为浏览起始路径也作为对话框的根路径传入。

2. 底层文件浏览器对话框

dialog_file_browser_show是固件系统级 API,声明于 applications/services/dialogs/dialogs.h,实现在 applications/services/dialogs/dialogs_api.c。也就是说,JS 脚本使用的文件选择器与原生 C 应用(如 Archive、Sub-GHz 等应用内部)所调用的是同一个文件浏览器组件,界面与交互完全一致。

3. DialogsFileBrowserOptions 可配置字段

DialogsFileBrowserOptions结构体(见 dialogs.h)完整定义了文件浏览器行为,JS 模块目前只使用了其中三字段:

字段类型JS 模块中的取值说明
extensionconst char*来自extension参数文件扩展名过滤
base_pathconst char*来自basePath参数根目录(按返回键时回到此处)
iconconst Icon*&I_file_10px文件列表项的图标

其余字段(如skip_assetshide_dot_fileshide_extselect_right等)在 JS 模块中被置零/默认,因为js_picker结构体是显式初始化的——这意味着 JS 层的pickFile目前不支持隐藏点文件、隐藏扩展名、右侧键选择等高级选项。如果未来需要这些能力,需要在 file_picker.c 的模块实现中扩展参数。

六、与其它 JS GUI 模块的协作建议

文件选择器在典型脚本中的定位是"前置步骤",常与以下模块组合使用:

  • gui/dialog:把选中路径展示给用户确认(如官方示例所示);
  • gui/text_input:在文件选择前让用户输入文件名前缀,实现"输入 + 选择"的复合流程;
  • storage(documentation/js/js_storage.md):对pickFile返回的路径执行读取、解析、写入等后续操作。

由于pickFile是同步阻塞调用,它会暂停车轮事件循环的处理;如果需要与event_loop定时器、后台任务并发,建议将文件选择放在用户触发(按键回调)的流程中执行,避免在初始化阶段长时间阻塞脚本启动。

七、常见问题速查

Q1:用户取消选择时返回什么?返回undefined。务必用if (path) { ... } else { ... }分支处理。

Q2:如何只让用户选择某种协议文件?把扩展名作为第二参数传入,如pickFile("/ext/subghz", ".sub")。可结合 documentation/js/js_gui.md 中关于模块导入的说明使用。

Q3:basePath不存在会怎样?文件浏览器会尽力在给定路径上初始化;为确保体验,建议传入已知存在的目录(如/ext),或先通过 storage 模块检查路径存在性。

Q4:gui/file_pickergui主模块的关系?它们是独立的模块单元,file_picker不需要实例化视图对象,直接require后调用pickFile即可;主gui模块仍用于视图调度(如viewDispatcher)。

总结

gui/file_picker是 Momentum-Firmware JS 运行时中最"轻量"的 GUI 模块之一:一个函数、两个参数、一个返回值,就完成了原生文件浏览器与 JS 脚本之间的桥接。它底层复用系统dialogs服务的dialog_file_browser_show,因此在 UI 与交互上与原生的文件管理体验完全一致。对脚本开发者而言,只需掌握basePath/extension的用法与undefined取消分支,即可稳定地把"用户选文件"这一交互集成到任何脚本中。

【免费下载链接】Momentum-Firmware🐬 Feature-rich, stable and customizable Flipper Firmware项目地址: https://gitcode.com/GitHub_Trending/mo/Momentum-Firmware

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/16 20:46:22

基于Neo4j与Spring Boot的化妆品知识图谱问答实践

简介:面向Java方向课程设计与知识图谱入门者,这份资源以化妆品领域为背景,完整覆盖知识图谱从数据采集、关系建模到智能问答的落地链路。项目图谱包含3000个节点、15000条边,覆盖口红与香水两类商品,支持图谱检索与智能…

作者头像 李华
网站建设 2026/9/16 20:46:20

Python实现个税计算器:财税与编程的完美结合

1. 项目概述:Python个税计算器的学习价值这个用纯Python实现的个人所得税模拟器,本质上是一个教学演示项目。它完整还原了国内现行个税计算规则,包括综合所得、专项扣除、累进税率等核心要素。对于财税专业学生和Python初学者而言&#xff0c…

作者头像 李华
网站建设 2026/9/16 20:46:12

Ubuntu apt报错Unable to locate package?根源排查与修复指南

你有没有遇到过这种情况:在Ubuntu里执行sudo apt-get install nginx结果终端直接甩回来一句E: Unable to locate package nginx然后你就开始怀疑人生:是不是系统装坏了?是不是没联网?是不是命令敲错了?我在帮别人排查问…

作者头像 李华
网站建设 2026/9/16 20:45:47

Regex101 里正则没匹配上?把表达式贴给走 TaoToken 的 Codex 核对

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/16 20:45:47

鸿蒙系统WebRTC音视频通话集成实战:选型、移植与踩坑记录

去年我们团队接到一个实时音视频通话的需求,要在鸿蒙系统上实现一对一视频通话功能。当时鸿蒙原生生态还不算成熟,社区里关于WebRTC在鸿蒙上的资料也少得可怜,踩了不少坑才把整个链路跑通。这篇文章把我从方案选型、环境搭建、核心链路实现到…

作者头像 李华