or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

directory-operations.mdfile-operations.mdindex.mdnetwork-operations.mdpickers.mdstreaming.mdsystem-paths.md
tile.json

pickers.mddocs/

File System Pickers

Native file and directory pickers for user-initiated file selection with platform-specific behaviors, MIME type filtering, and integration with system file managers.

Capabilities

File Picker Operations

Open native file selection dialogs for user-driven file access.

/**
 * Opens a file picker for user file selection
 * @param initialUri Optional initial directory path for picker
 * @param mimeType Optional MIME type filter for selectable files
 * @returns Promise resolving to selected File instance or array of Files
 */
static pickFileAsync(initialUri?: string, mimeType?: string): Promise<File | File[]>;

Usage Examples:

import { File } from "expo-file-system";

// Basic file picker
async function selectFile() {
  try {
    const selectedFile = await File.pickFileAsync();
    
    if (Array.isArray(selectedFile)) {
      console.log(`Selected ${selectedFile.length} files:`);
      selectedFile.forEach(file => {
        console.log(`- ${file.name} (${file.size} bytes)`);
      });
      return selectedFile;
    } else {
      console.log(`Selected file: ${selectedFile.name}`);
      console.log(`Size: ${selectedFile.size} bytes`);
      console.log(`Type: ${selectedFile.type}`);
      return [selectedFile];
    }
  } catch (error) {
    console.log("User cancelled file selection");
    return [];
  }
}

// Image picker with MIME type filter
async function selectImage() {
  try {
    const imageFile = await File.pickFileAsync(undefined, "image/*");
    
    if (Array.isArray(imageFile)) {
      return imageFile[0]; // Take first image if multiple selected
    }
    
    console.log(`Selected image: ${imageFile.name}`);
    console.log(`Image type: ${imageFile.type}`);
    return imageFile;
    
  } catch (error) {
    console.error("Image selection failed:", error);
    return null;
  }
}

// Document picker with specific MIME types
async function selectDocument() {
  const documentTypes = [
    "application/pdf",
    "application/msword",
    "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
    "text/plain"
  ].join(",");
  
  try {
    const document = await File.pickFileAsync(undefined, documentTypes);
    
    if (Array.isArray(document)) {
      console.log(`Selected ${document.length} documents`);
      return document;
    }
    
    console.log(`Selected document: ${document.name}`);
    return [document];
    
  } catch (error) {
    console.log("Document selection cancelled");
    return [];
  }
}

Directory Picker Operations

Open native directory selection dialogs for folder access (Android only).

/**
 * Opens a directory picker for user directory selection
 * @param initialUri Optional initial directory path for picker
 * @returns Promise resolving to selected Directory instance
 * @platform android
 */
static pickDirectoryAsync(initialUri?: string): Promise<Directory>;

Usage Examples:

import { Directory } from "expo-file-system";
import { Platform } from "react-native";

// Directory picker with platform check
async function selectDirectory(): Promise<Directory | null> {
  if (Platform.OS !== 'android') {
    console.warn("Directory picker is only available on Android");
    return null;
  }
  
  try {
    const selectedDirectory = await Directory.pickDirectoryAsync();
    
    console.log(`Selected directory: ${selectedDirectory.uri}`);
    console.log(`Directory name: ${selectedDirectory.name}`);
    
    // List contents of selected directory
    const contents = selectedDirectory.list();
    console.log(`Directory contains ${contents.length} items`);
    
    return selectedDirectory;
    
  } catch (error) {
    console.log("User cancelled directory selection");
    return null;
  }
}

// Directory picker with initial location
async function selectDirectoryWithInitial(initialPath: string): Promise<Directory | null> {
  if (Platform.OS !== 'android') {
    return null;
  }
  
  try {
    const selectedDirectory = await Directory.pickDirectoryAsync(initialPath);
    return selectedDirectory;
    
  } catch (error) {
    console.error("Directory selection failed:", error);
    return null;
  }
}

// Usage examples
async function demonstrateDirectoryPicker() {
  // Basic directory selection
  const userDirectory = await selectDirectory();
  
  if (userDirectory) {
    // Work with selected directory
    const files = userDirectory.list().filter(item => item instanceof File);
    console.log(`Found ${files.length} files in selected directory`);
  }
  
  // Select with initial path
  const documentsDirectory = await selectDirectoryWithInitial("/storage/emulated/0/Documents");
  
  if (documentsDirectory) {
    console.log(`Selected from Documents: ${documentsDirectory.name}`);
  }
}

File Type Filtering and Validation

Advanced file selection with type filtering and content validation.

Usage Examples:

import { File } from "expo-file-system";

// Media file picker with validation
async function selectMediaFile(): Promise<File | null> {
  try {
    const mediaFile = await File.pickFileAsync(undefined, "image/*,video/*,audio/*");
    
    if (Array.isArray(mediaFile)) {
      return mediaFile[0];
    }
    
    // Validate file size (e.g., max 50MB for media)
    const maxSize = 50 * 1024 * 1024;
    if (mediaFile.size > maxSize) {
      console.error(`File too large: ${mediaFile.size} bytes (max: ${maxSize})`);
      return null;
    }
    
    // Validate file type
    const allowedTypes = ['image/jpeg', 'image/png', 'video/mp4', 'audio/mp3'];
    if (!allowedTypes.includes(mediaFile.type)) {
      console.error(`Unsupported file type: ${mediaFile.type}`);
      return null;
    }
    
    console.log(`Valid media file selected: ${mediaFile.name}`);
    return mediaFile;
    
  } catch (error) {
    console.error("Media file selection failed:", error);
    return null;
  }
}

// CSV/Excel file picker with content preview
async function selectDataFile(): Promise<{ file: File; preview: string } | null> {
  const dataTypes = "text/csv,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
  
  try {
    const dataFile = await File.pickFileAsync(undefined, dataTypes);
    const file = Array.isArray(dataFile) ? dataFile[0] : dataFile;
    
    // Read first few lines for preview (CSV files)
    if (file.type === 'text/csv' || file.extension === '.csv') {
      const content = await file.text();
      const lines = content.split('\n').slice(0, 5); // First 5 lines
      const preview = lines.join('\n');
      
      console.log("CSV Preview:");
      console.log(preview);
      
      return { file, preview };
    }
    
    return { file, preview: `File: ${file.name} (${file.size} bytes)` };
    
  } catch (error) {
    console.log("Data file selection cancelled");
    return null;
  }
}

// Configuration file picker with validation
async function selectConfigFile(): Promise<File | null> {
  try {
    const configFile = await File.pickFileAsync(undefined, "application/json,text/xml,text/yaml");
    const file = Array.isArray(configFile) ? configFile[0] : configFile;
    
    // Validate file content based on extension
    const content = await file.text();
    
    if (file.extension === '.json') {
      try {
        JSON.parse(content);
        console.log("Valid JSON configuration file");
      } catch (e) {
        console.error("Invalid JSON format");
        return null;
      }
    } else if (file.extension === '.xml') {
      // Basic XML validation
      if (!content.includes('<?xml') || !content.includes('<')) {
        console.error("Invalid XML format");
        return null;
      }
    }
    
    return file;
    
  } catch (error) {
    console.error("Configuration file selection failed:", error);
    return null;
  }
}

File Processing Workflows

Complete workflows for file selection, processing, and storage.

Usage Examples:

import { File, Directory, Paths } from "expo-file-system";

// Image processing workflow
async function imageProcessingWorkflow(): Promise<File | null> {
  // Step 1: Select image
  const selectedImage = await File.pickFileAsync(undefined, "image/jpeg,image/png");
  if (!selectedImage || Array.isArray(selectedImage)) {
    console.log("No image selected");
    return null;
  }
  
  console.log(`Processing image: ${selectedImage.name}`);
  
  // Step 2: Create processing directory
  const processingDir = new Directory(Paths.document, "processed-images");
  processingDir.create({ idempotent: true });
  
  // Step 3: Copy to processing directory
  const processedImage = new File(
    processingDir, 
    `processed-${Date.now()}-${selectedImage.name}`
  );
  
  selectedImage.copy(processedImage);
  
  // Step 4: Add metadata file
  const metadataFile = new File(
    processingDir,
    `${processedImage.name}.metadata.json`
  );
  
  const metadata = {
    originalName: selectedImage.name,
    originalSize: selectedImage.size,
    processedAt: new Date().toISOString(),
    originalType: selectedImage.type
  };
  
  metadataFile.write(JSON.stringify(metadata, null, 2));
  
  console.log(`Image processing completed: ${processedImage.name}`);
  return processedImage;
}

// Document import workflow
async function documentImportWorkflow(): Promise<File[]> {
  const importedFiles: File[] = [];
  
  // Step 1: Select documents (potentially multiple)
  const selectedFiles = await File.pickFileAsync(
    undefined,
    "application/pdf,application/msword,text/plain"
  );
  
  const files = Array.isArray(selectedFiles) ? selectedFiles : [selectedFiles];
  
  // Step 2: Create import directory with timestamp
  const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
  const importDir = new Directory(Paths.document, "imports", timestamp);
  importDir.create({ intermediates: true });
  
  // Step 3: Process each file
  for (const file of files) {
    console.log(`Importing: ${file.name}`);
    
    // Create safe filename
    const safeFilename = file.name.replace(/[^a-zA-Z0-9.-]/g, '_');
    const importedFile = new File(importDir, safeFilename);
    
    // Copy file
    file.copy(importedFile);
    
    // Create import record
    const importRecord = {
      originalPath: file.uri,
      importedPath: importedFile.uri,
      originalSize: file.size,
      importedAt: new Date().toISOString(),
      checksum: file.info({ md5: true }).md5
    };
    
    const recordFile = new File(importDir, `${safeFilename}.import.json`);
    recordFile.write(JSON.stringify(importRecord, null, 2));
    
    importedFiles.push(importedFile);
  }
  
  // Step 4: Create import summary
  const summary = {
    importId: timestamp,
    fileCount: importedFiles.length,
    totalSize: importedFiles.reduce((sum, file) => sum + file.size, 0),
    files: importedFiles.map(file => ({
      name: file.name,
      size: file.size,
      type: file.type
    }))
  };
  
  const summaryFile = new File(importDir, "import-summary.json");
  summaryFile.write(JSON.stringify(summary, null, 2));
  
  console.log(`Import completed: ${importedFiles.length} files imported`);
  return importedFiles;
}

// Backup and restore workflow
async function backupWorkflow(): Promise<File | null> {
  // Select directory to backup (Android only)
  if (Platform.OS !== 'android') {
    console.log("Backup workflow requires Android for directory selection");
    return null;
  }
  
  try {
    // Step 1: Select source directory
    const sourceDir = await Directory.pickDirectoryAsync();
    console.log(`Backing up directory: ${sourceDir.name}`);
    
    // Step 2: Create backup location
    const backupDir = new Directory(Paths.document, "backups");
    backupDir.create({ idempotent: true });
    
    // Step 3: Create backup with timestamp
    const timestamp = new Date().toISOString().split('T')[0];
    const backupName = `${sourceDir.name}-backup-${timestamp}`;
    const targetBackupDir = new Directory(backupDir, backupName);
    
    // Step 4: Copy directory contents
    sourceDir.copy(targetBackupDir);
    
    // Step 5: Create backup manifest
    const manifest = {
      backupId: backupName,
      sourceDirectory: sourceDir.uri,
      backupDirectory: targetBackupDir.uri,
      createdAt: new Date().toISOString(),
      fileCount: targetBackupDir.list().length
    };
    
    const manifestFile = new File(backupDir, `${backupName}.manifest.json`);
    manifestFile.write(JSON.stringify(manifest, null, 2));
    
    console.log(`Backup completed: ${backupName}`);
    return manifestFile;
    
  } catch (error) {
    console.error("Backup workflow failed:", error);
    return null;
  }
}

// Usage demonstration
async function demonstrateWorkflows() {
  console.log("=== Image Processing Workflow ===");
  const processedImage = await imageProcessingWorkflow();
  
  console.log("\n=== Document Import Workflow ===");
  const importedDocs = await documentImportWorkflow();
  
  console.log("\n=== Backup Workflow ===");
  const backupManifest = await backupWorkflow();
  
  return {
    processedImage,
    importedDocs,
    backupManifest
  };
}

Platform-Specific Considerations

Handle platform differences and optimize picker behavior.

Usage Examples:

import { File, Directory } from "expo-file-system";
import { Platform } from "react-native";

// Platform-adaptive file picker
class PlatformFilePicker {
  static async pickFile(options: {
    mimeTypes?: string[];
    multiple?: boolean;
    initialDirectory?: string;
  } = {}): Promise<File[]> {
    
    const mimeType = options.mimeTypes?.join(',');
    
    try {
      if (Platform.OS === 'ios') {
        // iOS-specific behavior
        console.log("Using iOS file picker");
        const result = await File.pickFileAsync(options.initialDirectory, mimeType);
        return Array.isArray(result) ? result : [result];
        
      } else if (Platform.OS === 'android') {
        // Android-specific behavior
        console.log("Using Android file picker");
        const result = await File.pickFileAsync(options.initialDirectory, mimeType);
        return Array.isArray(result) ? result : [result];
        
      } else {
        throw new Error(`Unsupported platform: ${Platform.OS}`);
      }
      
    } catch (error) {
      console.log("File picker cancelled or failed");
      return [];
    }
  }
  
  static async pickDirectory(initialPath?: string): Promise<Directory | null> {
    if (Platform.OS !== 'android') {
      console.warn("Directory picker only available on Android");
      // Could implement alternative for iOS using document picker
      return null;
    }
    
    try {
      return await Directory.pickDirectoryAsync(initialPath);
    } catch (error) {
      console.log("Directory picker cancelled");
      return null;
    }
  }
}

// Cross-platform file access patterns
async function crossPlatformFileAccess() {
  // Adapt picker behavior to platform capabilities
  const files = await PlatformFilePicker.pickFile({
    mimeTypes: ['image/jpeg', 'image/png'],
    multiple: Platform.OS === 'android' // Multiple selection on Android
  });
  
  console.log(`Selected ${files.length} files`);
  
  // Platform-specific directory access
  let workingDirectory: Directory | null = null;
  
  if (Platform.OS === 'android') {
    // Use directory picker on Android
    workingDirectory = await PlatformFilePicker.pickDirectory();
  } else {
    // Use predefined directory on iOS
    workingDirectory = new Directory(Paths.document, "user-files");
    workingDirectory.create({ idempotent: true });
  }
  
  if (workingDirectory) {
    console.log(`Working with directory: ${workingDirectory.name}`);
    
    // Process selected files in working directory
    for (const file of files) {
      const targetFile = new File(workingDirectory, file.name);
      file.copy(targetFile);
      console.log(`Copied ${file.name} to working directory`);
    }
  }
}

// Usage
await crossPlatformFileAccess();