📁 File Operations
Học cách làm việc với files: chọn file, đọc/ghi, và upload. ⏱️ 20 phút
🎯 Mục tiêu
Phần tiêu đề “🎯 Mục tiêu”Nắm vững:
- FilePicker - Chọn files
- Đọc và ghi files
- Upload files
- Directory picker
📂 FilePicker với Declarative UI
Phần tiêu đề “📂 FilePicker với Declarative UI”import flet as ft
@ft.observableclass FileState: selected_files: str = ""
def set_files(self, files_text: str): self.selected_files = files_text
state = FileState()
@ft.componentdef FilePickerDemo(file_picker: ft.FilePicker): return ft.Column([ ft.ElevatedButton( "Pick files", icon=ft.Icons.UPLOAD_FILE, on_click=lambda _: file_picker.pick_files(allow_multiple=True), ), ft.Text(state.selected_files or "No files selected"), ], spacing=20)
def main(page: ft.Page): page.title = "File Picker"
def pick_files_result(e: ft.FilePickerResultEvent): if e.files: state.set_files(", ".join([f.name for f in e.files])) else: state.set_files("Cancelled")
file_picker = ft.FilePicker(on_result=pick_files_result) page.overlay.append(file_picker)
page.add(FilePickerDemo(file_picker))
ft.run(main)🔍 Lọc theo loại file
Phần tiêu đề “🔍 Lọc theo loại file”import flet as ft
@ft.observableclass ImagePickerState: selected_image: str = "" image_name: str = ""
state = ImagePickerState()
@ft.componentdef ImagePickerDemo(file_picker: ft.FilePicker): return ft.Column([ ft.Container( content=ft.Image(src=state.selected_image, fit=ft.ImageFit.CONTAIN) if state.selected_image else ft.Text("No image"), width=300, height=300, bgcolor=ft.Colors.GREY_200, alignment=ft.alignment.center, border_radius=10, ), ft.Text(state.image_name) if state.image_name else None, ft.ElevatedButton( "Choose image", icon=ft.Icons.IMAGE, on_click=lambda _: file_picker.pick_files( allowed_extensions=["png", "jpg", "jpeg", "gif"] ), ), ], spacing=15, horizontal_alignment=ft.CrossAxisAlignment.CENTER)
def main(page: ft.Page): page.title = "Image Picker"
def pick_image(e: ft.FilePickerResultEvent): if e.files: state.selected_image = e.files[0].path state.image_name = e.files[0].name
file_picker = ft.FilePicker(on_result=pick_image) page.overlay.append(file_picker)
page.vertical_alignment = ft.MainAxisAlignment.CENTER page.horizontal_alignment = ft.CrossAxisAlignment.CENTER page.add(ImagePickerDemo(file_picker))
ft.run(main)📖 Đọc nội dung file
Phần tiêu đề “📖 Đọc nội dung file”import flet as ft
@ft.observableclass TextFileState: content: str = "" file_name: str = "" error: str = ""
def set_content(self, name: str, content: str): self.file_name = name self.content = content self.error = ""
def set_error(self, error: str): self.error = error self.content = "" self.file_name = ""
state = TextFileState()
@ft.componentdef TextFileReader(file_picker: ft.FilePicker): return ft.Column([ ft.ElevatedButton( "Open text file", icon=ft.Icons.FILE_OPEN, on_click=lambda _: file_picker.pick_files( allowed_extensions=["txt", "md", "py"] ), ), ft.Text(state.file_name, weight=ft.FontWeight.BOLD) if state.file_name else None, ft.Container( content=ft.Text(state.content, selectable=True) if state.content else ft.Text(state.error, color=ft.Colors.RED) if state.error else ft.Text("Select a file to view content", color=ft.Colors.GREY), bgcolor=ft.Colors.GREY_100, padding=20, width=500, border_radius=10, ), ], spacing=15)
def main(page: ft.Page): page.title = "Text File Reader"
def pick_result(e: ft.FilePickerResultEvent): if e.files: file_path = e.files[0].path try: with open(file_path, "r", encoding="utf-8") as f: content = f.read() state.set_content(e.files[0].name, content[:1000]) # First 1000 chars except Exception as ex: state.set_error(f"Error: {ex}")
file_picker = ft.FilePicker(on_result=pick_result) page.overlay.append(file_picker)
page.padding = 30 page.add(TextFileReader(file_picker))
ft.run(main)💾 Lưu file (Save As)
Phần tiêu đề “💾 Lưu file (Save As)”import flet as ft
@ft.observableclass EditorState: content: str = "" status: str = ""
def set_content(self, text: str): self.content = text
def set_status(self, status: str): self.status = status
state = EditorState()
@ft.componentdef TextEditor(file_picker: ft.FilePicker): return ft.Column([ ft.TextField( value=state.content, multiline=True, min_lines=8, hint_text="Enter your text here...", on_change=lambda e: state.set_content(e.control.value), ), ft.Row([ ft.ElevatedButton( "Save As", icon=ft.Icons.SAVE, on_click=lambda _: file_picker.save_file( file_name="document.txt", allowed_extensions=["txt"], ), ), ft.Text(state.status, color=ft.Colors.GREEN if "Saved" in state.status else ft.Colors.GREY), ]), ], spacing=15, expand=True)
def main(page: ft.Page): page.title = "Text Editor" page.padding = 30
def save_result(e: ft.FilePickerResultEvent): if e.path: try: with open(e.path, "w", encoding="utf-8") as f: f.write(state.content) state.set_status(f"✅ Saved to: {e.path}") except Exception as ex: state.set_status(f"❌ Error: {ex}") else: state.set_status("Cancelled")
file_picker = ft.FilePicker(on_result=save_result) page.overlay.append(file_picker)
page.add(TextEditor(file_picker))
ft.run(main)📁 Chọn thư mục
Phần tiêu đề “📁 Chọn thư mục”import flet as ft
@ft.observableclass FolderState: selected_path: str = ""
state = FolderState()
@ft.componentdef FolderPicker(file_picker: ft.FilePicker): return ft.Column([ ft.ElevatedButton( "Select Folder", icon=ft.Icons.FOLDER_OPEN, on_click=lambda _: file_picker.get_directory_path(), ), ft.Container( content=ft.Text(state.selected_path or "No folder selected"), bgcolor=ft.Colors.GREY_100, padding=15, border_radius=8, width=400, ), ], spacing=15)
def main(page: ft.Page): page.title = "Folder Picker" page.padding = 30
def pick_folder(e: ft.FilePickerResultEvent): if e.path: state.selected_path = e.path
file_picker = ft.FilePicker(on_result=pick_folder) page.overlay.append(file_picker)
page.add(FolderPicker(file_picker))
ft.run(main)⬆️ Upload file (Web)
Phần tiêu đề “⬆️ Upload file (Web)”Cho ứng dụng web, sử dụng upload:
import flet as ft
@ft.observableclass UploadState: is_uploading: bool = False status: str = "" progress: float = 0
def start_upload(self): self.is_uploading = True self.status = "Uploading..." self.progress = 0
def update_progress(self, progress: float, file_name: str): self.progress = progress if progress >= 1: self.is_uploading = False self.status = f"✅ Uploaded: {file_name}"
state = UploadState()
@ft.componentdef FileUploader(file_picker: ft.FilePicker): return ft.Column([ ft.ElevatedButton( "Upload", icon=ft.Icons.CLOUD_UPLOAD, on_click=lambda _: file_picker.pick_files(allow_multiple=True), ), ft.ProgressRing(visible=state.is_uploading), ft.ProgressBar(value=state.progress, visible=state.is_uploading), ft.Text(state.status), ], spacing=15)
def main(page: ft.Page): page.title = "File Uploader" page.padding = 30
def upload_files(e: ft.FilePickerResultEvent): if e.files: state.start_upload() for f in e.files: upload_list = [ ft.FilePickerUploadFile( f.name, upload_url=page.get_upload_url(f.name, 600), ) ] file_picker.upload(upload_list)
def upload_progress(e: ft.FilePickerUploadEvent): state.update_progress(e.progress, e.file_name)
file_picker = ft.FilePicker( on_result=upload_files, on_upload=upload_progress, ) page.overlay.append(file_picker)
page.add(FileUploader(file_picker))
ft.run(main, upload_dir="uploads")📋 FilePicker Methods
Phần tiêu đề “📋 FilePicker Methods”| Method | Description |
|---|---|
pick_files() |
Chọn files |
save_file() |
Lưu file (Save As) |
get_directory_path() |
Chọn thư mục |
upload() |
Upload files (web) |
⏭️ Tiếp theo
Phần tiêu đề “⏭️ Tiếp theo”Tuyệt! Tiếp tục với Async Operations để xử lý bất đồng bộ.