CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-expo-file-system

Provides comprehensive access to the local file system on mobile devices through Expo's module system.

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

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();

docs

directory-operations.md

file-operations.md

index.md

network-operations.md

pickers.md

streaming.md

system-paths.md

tile.json