Flet FilePickerResultEvent 详解:文件选择结果的类型化事件负载
2026/9/24 12:59:37 网站建设 项目流程
  • 前端
  • 跨平台
  • 桌面应用
  • 移动开发

【免费下载链接】flet

Build realtime web, mobile and desktop apps in Python only. No frontend experience required.

项目地址:https://gitcode.com/gh_mirrors/fl/flet
点击查看免费下载

导读

FilePickerResultEvent是 Flet 文件选择服务中负责承载选择结果的核心事件类型:当用户通过PickFiles客户端动作完成文件选择(或取消选择)后,Flet 会构造该事件对象并传递给FilePicker.on_result回调。本文以 Flet SDK 的源码实现为据,完整讲解该事件类的字段语义、触发时机、与pick_files()返回值的区别,并结合仓库示例给出可直接运行的实战代码与 Web 端最佳实践。

什么是 FilePickerResultEvent

类定义与继承关系

在 Flet 中,FilePickerResultEvent定义于 sdk/python/packages/flet/src/flet/controls/services/file_picker.py(第 146-159 行),是一个继承自Event["FilePicker"]的 dataclass:

@dataclass class FilePickerResultEvent(Event["FilePicker"]): """ Event emitted when files are selected through a :class:`~flet.PickFiles` action. Not emitted by :meth:`flet.FilePicker.pick_files`, which returns the selected files directly. """ files: list[FilePickerFile] """ The selected files, or an empty list if the user cancelled. """

它继承的基类Event定义于 sdk/python/packages/flet/src/flet/controls/control_event.py(第 108-136 行),是 Flet 所有控件事件回调的统一负载类型,本身携带三个通用字段:

  • name:事件名称字符串;
  • data:可选的原始事件数据;
  • control:触发该事件的控件实例(即FilePicker本身)。

基于Event基类,FilePickerResultEvent通过control字段即可访问其所属的FilePicker,并通过page属性获得持有该控件的Page对象。在此基础上,FilePickerResultEvent增加了一个独有的业务字段files,这也是它在实际开发中唯一需要直接操作的属性。

官方文档中的呈现方式

仓库中对应的类型文档页本身仅是一个占位文件,它通过引入ClassAll组件(实现于 website/src/components/crocodocs/ClassAll.js)把该类的完整 API 文档(docstring、成员签名、字段说明)从 SDK 源码动态渲染到文档站中。因此,本文所述的字段与行为细节,其权威来源正是上面列出的 SDK 源码。

核心字段:files

files的类型为list[FilePickerFile],是本次选择得到的文件元数据列表,其语义包含两个关键点:

  1. 选择成功时:列表包含用户选中的全部文件,每个元素都是一个FilePickerFile对象;
  2. 用户取消时:列表为空([]),而不是None

与旧版 Flet(如 2022 年发布文件选择功能时的行为,见博客文章)相比,当前版本的语义已经明确收敛:FilePickerResultEvent不再承载path等字段,save_file()get_directory_path()的路径结果改为由方法返回值直接提供,事件只负责“Pick files”场景的选择结果。

FilePickerFile 的字段构成

files列表中的每个元素是FilePickerFile(定义于同一文件的第 98-143 行),其字段如下:

字段类型说明
idintFlet 分配的选择级文件标识,仅在当前选择会话内稳定,上传匹配时优先使用
namestr文件名(basename),不含目录路径
sizeint文件大小(字节)
pathstr \| None文件的绝对路径;Web 模式恒为None,原生平台上若系统选择器不暴露路径也可能为None
bytesbytes \| None文件内容;仅当pick_files(with_data=True)时才有值,否则为None

空列表的判定技巧

由于取消选择时files是空列表而非None,判断用户是否取消可以简单地使用真假值判定,或者显式检查len(e.files) == 0

def handle_result(e: ft.FilePickerResultEvent): if not e.files: print("Selection cancelled") return for f in e.files: print(f"Picked: {f.name} ({f.size} bytes)")

触发时机:on_result 回调

注册方式

FilePickerResultEvent通过FilePicker.on_result事件处理器被回调。在创建FilePicker实例时传入即可:

import flet as ft def handle_result(e: ft.FilePickerResultEvent): # e.files 即为本次选择的文件列表 ... file_picker = ft.FilePicker(on_result=handle_result) page.services.append(file_picker)

什么场景会触发它

源码 docstring 明确强调:该事件仅由PickFiles客户端动作触发,pick_files()方法不会触发它——因为pick_files()是异步方法,选择结果直接作为返回值返回给调用方(见 file_picker.py 第 333-392 行,方法返回list[FilePickerFile])。

PickFiles是一个ClientAction(同文件第 401-485 行),它把文件选择对话框的打开动作绑定在控件自身,由客户端在用户的原始手势(click / key press)内完成,这正是浏览器唯一允许打开文件选择器的时机。选择结果无法同步返回给 Python 调用方,于是通过on_result事件送达——也就是FilePickerResultEvent的核心使用场景。

源码对两种方式的结果获取路径做了清晰的对比:

获取方式结果返回途径适用场景
await file_picker.pick_files(...)方法返回值list[FilePickerFile]桌面端、按钮on_click中直接调用
ft.PickFiles(file_picker, ...)动作FilePicker.on_result回调(即FilePickerResultEventWeb 端(尤其是 iOS Safari),必须保持浏览器手势

Web 端必须使用动作的原因

在 Web 应用中,浏览器只会在处理点击或按键事件时打开文件选择器。如果在on_click回调里调用pick_files(),点击事件已经先传到 Python 代码、指令再传回浏览器,此时“用户手势”的权限已失效:Safari 会静默拒绝,Chrome 和 Firefox 则允许。这导致同一应用往往在除 iPhone/iPad 外的所有平台都能工作。因此官方文档(filepicker.md)建议将PickFiles动作挂到控件上,让对话框在原始手势内打开,并在on_result中处理选择结果。

实战:基于 on_result 的多文件选择与上传

下面的示例取自仓库示例 pick_files_action/main.py,完整演示了FilePickerResultEvent的典型用法——通过PickFiles动作触发选择,在on_result中读取e.files展示文件列表,最后把文件交给upload()上传:

import flet as ft from dataclasses import dataclass, field @dataclass class State: picked_files: list[ft.FilePickerFile] = field(default_factory=list) state = State() def main(page: ft.Page): prog_bars: dict[str, ft.ProgressRing] = {} def handle_upload_progress(e: ft.FilePickerUploadEvent): prog_bars[e.file_name].value = e.progress def handle_result(e: ft.FilePickerResultEvent): # PickFiles 动作在 Python 看到点击之前就打开了对话框, # 所以选择结果在这里到达,而不是返回给调用方。 state.picked_files = e.files prog_bars.clear() upload_progress.controls.clear() for f in e.files: prog = ft.ProgressRing(value=0, bgcolor="#eeeeee", width=20, height=20) prog_bars[f.name] = prog upload_progress.controls.append( ft.Row([prog, ft.Text(f"{f.name} ({f.size} bytes)")]) ) upload_button.disabled = len(e.files) == 0 async def handle_file_upload(e: ft.Event[ft.Button]): upload_button.disabled = True # 被选中的文件仍然关联在 FilePicker 上,upload() 直接使用它们。 await file_picker.upload( files=[ ft.FilePickerUploadFile( name=file.name, upload_url=page.get_upload_url(f"dir/{file.name}", 60), ) for file in state.picked_files ] ) file_picker = ft.FilePicker( on_result=handle_result, on_upload=handle_upload_progress, ) page.services.append(file_picker) page.add( ft.SafeArea( content=ft.Column( controls=[ ft.Button( content="Select files...", icon=ft.Icons.FOLDER_OPEN, # 把选择动作挂在控件上——而不是在 on_click 里调用 # pick_files()——是对话框在 iOS 上能够打开的关键。 action=ft.PickFiles(file_picker, allow_multiple=True), ), upload_progress := ft.Column(), upload_button := ft.Button( content="Upload", icon=ft.Icons.UPLOAD, on_click=handle_file_upload, disabled=True, ), ], ), ) ) if __name__ == "__main__": ft.run(main, upload_dir="examples")

运行方式(示例文件头部注明):

export FLET_SECRET_KEY=<some_secret_key> uv run flet run --web examples/services/file_picker/pick_files_action/main.py

代码中有几个值得注意的实战细节:

  • e.files中的每个文件都带有namesize,可直接用于渲染文件列表;handle_result中通过len(e.files) == 0判断是否禁用了上传按钮,与空列表语义一致;
  • 选中的文件仍关联在FilePicker上,因此upload()无需重新接收文件对象,只需按name(或id)构造FilePickerUploadFile并给出upload_url
  • page.get_upload_url(f"dir/{file.name}", 60)生成一个 60 秒有效的预签名上传 URL,dir/前缀目录会在upload_dir下自动创建。

与 pick_files() 返回值模式的对比

为了彻底理解FilePickerResultEvent的定位,下面再对比另一种不使用事件的结果获取方式。示例 pick_and_upload/main.py 展示了await file_picker.pick_files(allow_multiple=True)直接拿到list[FilePickerFile]的写法(该示例仅支持 Web 模式运行):

async def handle_files_pick(e: ft.Event[ft.Button]): state.file_picker = ft.FilePicker(on_upload=on_upload_progress) files = await state.file_picker.pick_files(allow_multiple=True) print("Picked files:", files) state.picked_files = files upload_button.disabled = len(files) == 0 ...

两种模式的本质区别在于结果传递通道

  • pick_files()的返回值是一次性、同步地在await处取得,调用方代码流清晰;
  • FilePickerResultEvent则是事件驱动的异步回调,适用于无法把结果直接返回给调用方的PickFiles动作场景。

需要文件内容的场景

示例 pick_and_save_text_content/main.py 展示了with_data=True的用法——在 Web 模式下FilePickerFile.path不可用,但bytes字段携带了文件内容,可以直接解码读取:

files = await ft.FilePicker().pick_files( allow_multiple=False, with_data=True, file_type=ft.FilePickerFileType.CUSTOM, allowed_extensions=["txt", "md"], ) if not files: ... selected = files[0] selected_file_content.value = ( selected.bytes.decode("utf-8", errors="replace") if selected.bytes else "" )

对应地,若FilePickerResultEvent场景下需要文件内容,只需在PickFiles动作上设置with_data=True(该动作在 file_picker.py 第 456-459 行将with_data透传给pick_files调用),随后在on_result中通过f.bytes读取。

总结

FilePickerResultEvent是 Flet 文件选择在“客户端动作”模式下的结果载体,它把PickFiles打开对话框后用户的选择以files: list[FilePickerFile]的形式送达FilePicker.on_result。理解它的关键在于三点:

  1. 触发源唯一:只有PickFiles动作会触发它,pick_files()直接返回列表而不触发事件;
  2. 空列表表示取消:用户取消时e.files[],用真假值即可判断;
  3. 与上传流程衔接:选中的文件始终与FilePicker关联,事件处理完成后可直接构造FilePickerUploadFile调用upload()

在 Web(尤其是 iOS)环境中,把PickFiles动作挂在按钮上、在on_result里消费FilePickerResultEvent,是让文件选择功能稳定工作的推荐做法。如需查看更多字段与完整 API,可继续查阅 FilePickerFile 类型文档、FilePickerUploadEvent 类型文档 以及 FilePicker 服务文档。

  • 前端
  • 跨平台
  • 桌面应用
  • 移动开发

【免费下载链接】flet

Build realtime web, mobile and desktop apps in Python only. No frontend experience required.

项目地址:https://gitcode.com/gh_mirrors/fl/flet
点击查看免费下载

相关推荐

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询