ScaleRocket/Mobile

Sélecteur d'images

Utilisation d'expo-image-picker pour sélectionner des images de la galerie, les compresser et les uploader vers votre backend.

Sélecteur d'images

expo-image-picker permet aux utilisateurs de sélectionner des photos de leur galerie ou d'en prendre de nouvelles. ScaleRocket Mobile l'utilise pour les avatars, les images de posts et les pièces jointes.

Installation

npx expo install expo-image-picker

Ajoutez les descriptions de permission dans app.json :

{
  "expo": {
    "ios": {
      "infoPlist": {
        "NSPhotoLibraryUsageDescription": "We access your photos to let you upload a profile picture"
      }
    }
  }
}

Sélectionner une image

import * as ImagePicker from "expo-image-picker";

async function pickImage() {
  const result = await ImagePicker.launchImageLibraryAsync({
    mediaTypes: ImagePicker.MediaTypeOptions.Images,
    allowsEditing: true,
    aspect: [1, 1],
    quality: 0.8,
  });

  if (!result.canceled) {
    const image = result.assets[0];
    console.log("Selected:", image.uri);
    return image;
  }
}
OptionValeurObjectif
mediaTypesImages, Videos, AllFiltrer le type de média
allowsEditingtrueAfficher l'UI de recadrage/redimensionnement
aspect[1, 1]Ratio de recadrage (iOS uniquement)
quality0.0 - 1.0Compression de l'image

Prendre une photo

Utilisez launchCameraAsync pour ouvrir la caméra directement :

async function takePhoto() {
  const permission = await ImagePicker.requestCameraPermissionsAsync();
  if (!permission.granted) {
    Alert.alert("Permission required", "Camera access is needed to take photos");
    return;
  }

  const result = await ImagePicker.launchCameraAsync({
    allowsEditing: true,
    aspect: [1, 1],
    quality: 0.8,
  });

  if (!result.canceled) {
    return result.assets[0];
  }
}

Compression d'image

Compressez les images avant l'upload pour réduire la bande passante et le stockage :

import { manipulateAsync, SaveFormat } from "expo-image-manipulator";

async function compressImage(uri: string) {
  const result = await manipulateAsync(
    uri,
    [{ resize: { width: 800 } }],
    { compress: 0.7, format: SaveFormat.JPEG }
  );

  return result.uri;
}
npx expo install expo-image-manipulator

Upload vers le backend

Upload vers Supabase Storage :

import { supabase } from "@/lib/supabase";

async function uploadImage(uri: string, userId: string) {
  const compressedUri = await compressImage(uri);
  const fileName = `${userId}/avatar-${Date.now()}.jpg`;

  const formData = new FormData();
  formData.append("file", {
    uri: compressedUri,
    name: "avatar.jpg",
    type: "image/jpeg",
  } as any);

  const { data, error } = await supabase.storage
    .from("avatars")
    .upload(fileName, formData, { upsert: true });

  if (error) throw error;

  const { data: urlData } = supabase.storage
    .from("avatars")
    .getPublicUrl(fileName);

  return urlData.publicUrl;
}

Upload vers Convex File Storage :

import { useMutation } from "convex/react";
import { api } from "@/convex/_generated/api";

async function uploadImage(uri: string) {
  const compressedUri = await compressImage(uri);

  // Obtenir l'URL d'upload
  const uploadUrl = await generateUploadUrl();

  // Uploader le fichier
  const response = await fetch(compressedUri);
  const blob = await response.blob();

  const result = await fetch(uploadUrl, {
    method: "POST",
    body: blob,
    headers: { "Content-Type": "image/jpeg" },
  });

  const { storageId } = await result.json();
  return storageId;
}

Composant complet d'upload d'avatar

function AvatarUpload({ user }: { user: User }) {
  const [uploading, setUploading] = useState(false);
  const [avatarUri, setAvatarUri] = useState(user.avatarUrl);

  const handleUpload = async () => {
    const image = await pickImage();
    if (!image) return;

    setUploading(true);
    try {
      const url = await uploadImage(image.uri, user.id);
      setAvatarUri(url);
    } catch (error) {
      Alert.alert("Upload Failed", "Could not upload image. Try again.");
    } finally {
      setUploading(false);
    }
  };

  return (
    <TouchableOpacity onPress={handleUpload} disabled={uploading}>
      <Avatar source={avatarUri ? { uri: avatarUri } : undefined} name={user.name} size="lg" />
      {uploading && <ActivityIndicator style={styles.overlay} />}
    </TouchableOpacity>
  );
}

Sélection multiple d'images

Sélectionnez plusieurs images à la fois :

const result = await ImagePicker.launchImageLibraryAsync({
  mediaTypes: ImagePicker.MediaTypeOptions.Images,
  allowsMultipleSelection: true,
  selectionLimit: 5,
  quality: 0.8,
});

if (!result.canceled) {
  const images = result.assets; // Tableau d'images sélectionnées
}

Notes

  • allowsEditing ne supporte que la sélection d'une seule image
  • Sur iOS, le ratio aspect n'est appliqué que quand allowsEditing est true
  • Les grandes images doivent toujours être compressées avant l'upload
  • Le sélecteur d'images fonctionne dans Expo Go (contrairement à la caméra)

On this page