Chapter 6 of 12 ~18 min read

Camera, Gallery & Filesystem

Migrating from cordova-plugin-camera & cordova-plugin-file to expo-camera, expo-image-picker, expo-file-system.


1. Modern Permission & Gallery Flow

On modern Android (Android 14, 15, 16), scoped storage prevents apps from reading arbitrary disk paths. Cordova apps frequently crashed due to outdated file URIs. With expo-image-picker, runtime permissions and temporary secure cached URIs are handled automatically.

React Native Complete Image Picker Component
import React, { useState } from 'react';
import { View, Image, StyleSheet, Button, Alert } from 'react-native';
import * as ImagePicker from 'expo-image-picker';
import * as FileSystem from 'expo-file-system';

export function MediaPickerScreen() {
  const [imageUri, setImageUri] = useState<string | null>(null);

  const pickImage = async () => {
    // 1. Request permission
    const { status } = await ImagePicker.requestMediaLibraryPermissionsAsync();
    if (status !== 'granted') {
      Alert.alert('Permission Denied', 'We need access to your gallery.');
      return;
    }

    // 2. Launch native picker
    const result = await ImagePicker.launchImageLibraryAsync({
      mediaTypes: ['images'],
      allowsEditing: true,
      aspect: [4, 3],
      quality: 0.8,
    });

    if (!result.canceled) {
      setImageUri(result.assets[0].uri);
      
      // 3. Inspect file info via expo-file-system
      const info = await FileSystem.getInfoAsync(result.assets[0].uri);
      console.log('File size in bytes:', info.size);
    }
  };

  return (
    <View style={styles.container}>
      <Button title="Select Photo from Gallery" onPress={pickImage} />
      {imageUri && <Image source={{ uri: imageUri }} style={styles.image} />}
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, padding: 20, alignItems: 'center', justifyContent: 'center' },
  image: { width: 300, height: 225, marginTop: 20, borderRadius: 12 }
});