# file-download > File download implementation pattern for Tauri applications. Use this skill when implementing file save/download functionality in PhotoClove. - Author: Atsushi Kato - Repository: ktat/photoclove - Version: 20260202093019 - Stars: 0 - Forks: 0 - Last Updated: 2026-02-06 - Source: https://github.com/ktat/photoclove - Web: https://mule.run/skillshub/@@ktat/photoclove~file-download:20260202093019 --- --- name: file-download description: File download implementation pattern for Tauri applications. Use this skill when implementing file save/download functionality in PhotoClove. --- # File Download Implementation PhotoClove uses Tauri backend to save files to the user's download directory. This pattern is required because browser-based download approaches don't work reliably in Tauri. ## Architecture Overview ``` Frontend (React) Backend (Rust/Tauri) │ │ │ 1. Convert Blob to │ │ Base64 string │ │ │ │ 2. Invoke Tauri command │ │ ────────────────────────> │ │ │ │ │ 3. Decode Base64 │ │ 4. Write to Download dir │ │ 5. Return saved path │ <──────────────────────── │ │ │ │ 6. Show success feedback │ ``` ## Frontend Implementation ### 1. Save Function (ShareUtils.js) ```javascript import { invoke } from '@tauri-apps/api/core'; export async function saveImageAsFile(blob, filename = 'image.png') { // Convert blob to base64 const arrayBuffer = await blob.arrayBuffer(); const uint8Array = new Uint8Array(arrayBuffer); let binary = ''; for (let i = 0; i < uint8Array.length; i++) { binary += String.fromCharCode(uint8Array[i]); } const base64Data = btoa(binary); // Save via Tauri command const savedPath = await invoke('save_image_to_download_dir', { imageData: base64Data, filename: filename }); return savedPath; } ``` ### 2. Button Handler (Component) **IMPORTANT**: The handler MUST be `async` and use `await`: ```javascript const [saveStatus, setSaveStatus] = useState(null); const handleSaveImage = useCallback(async () => { if (!imageBlob) return; const date = new Date().toISOString().split('T')[0]; const filename = `photoclove-image-${date}.png`; try { await saveImageAsFile(imageBlob, filename); setSaveStatus('saved'); setTimeout(() => setSaveStatus(null), 2000); } catch (error) { logger.error('Component', 'save_error', 'Failed to save', { error: error.message }); setSaveStatus('error'); setTimeout(() => setSaveStatus(null), 2000); } }, [imageBlob]); ``` ### 3. Button UI with Feedback ```jsx ``` ## Backend Implementation (Rust) ### Tauri Command ```rust // In src-tauri/src/commands/file_commands.rs or similar #[tauri::command] pub async fn save_image_to_download_dir( image_data: String, // Base64 encoded filename: String, ) -> Result { use base64::{Engine as _, engine::general_purpose}; use std::fs; // Get download directory let download_dir = dirs::download_dir() .ok_or("Could not find download directory")?; // Decode base64 let bytes = general_purpose::STANDARD .decode(&image_data) .map_err(|e| format!("Failed to decode base64: {}", e))?; // Create full path let file_path = download_dir.join(&filename); // Write file fs::write(&file_path, &bytes) .map_err(|e| format!("Failed to write file: {}", e))?; log::info!(target: "file", "save_image; path={}", file_path.display()); Ok(file_path.to_string_lossy().to_string()) } ``` ### Register Command in lib.rs ```rust .invoke_handler(tauri::generate_handler![ // ... other commands save_image_to_download_dir, ]) ``` ## Common Mistakes ### 1. Missing `async`/`await` ```javascript // ❌ BAD - Won't wait for save to complete const handleSave = () => { saveImageAsFile(blob, 'file.png'); }; // ✅ GOOD - Properly waits and handles result const handleSave = async () => { await saveImageAsFile(blob, 'file.png'); }; ``` ### 2. No Error Handling ```javascript // ❌ BAD - Silent failure const handleSave = async () => { await saveImageAsFile(blob, 'file.png'); }; // ✅ GOOD - Catches and reports errors const handleSave = async () => { try { await saveImageAsFile(blob, 'file.png'); // success feedback } catch (error) { // error feedback } }; ``` ### 3. No User Feedback Always show: - Success state ("Saved!") - Error state if save fails - Loading state for large files ## File Locations | File | Purpose | |------|---------| | `src/utils/ShareUtils.js` | `saveImageAsFile()` function | | `src-tauri/src/commands/file_commands.rs` | Tauri command implementation | | `src-tauri/src/lib.rs` | Command registration | ## Testing 1. Generate or select an image 2. Click Save button 3. Check Downloads folder for the file 4. Verify button shows "Saved!" feedback