Galereya — rasm tanlash va serverga yuklash

~15 daq · React Native · Qurilma imkoniyatlari

Muammodan boshlaylik

Profil rasmini o'zgartirish. Foydalanuvchi:

  1. "Rasmni o'zgartirish" ni bosadi
  2. Galereyadan tanlaydi yoki darhol suratga oladi
  3. Rasmni kvadrat qilib kesadi
  4. Rasm serverga yuklanadi — progress bilan

Yana bir muammo: zamonaviy telefon rasmi 4000×3000 piksel, 5-8 MB. Uni mobil internet orqali yuklash — sekin va foydalanuvchi trafigini yeydi. Profil rasmi uchun esa 400×400 yetarli.


O'rnatish

npx expo install expo-image-picker expo-image-manipulator
"plugins": [
  ["expo-image-picker", {
    "photosPermission": "Profil rasmini tanlash uchun galereyaga kirish kerak.",
    "cameraPermission": "Profil rasmini suratga olish uchun kamera kerak."
  }]
]

Galereyadan tanlash

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

async function galereyadanTanla() {
  const natija = await ImagePicker.launchImageLibraryAsync({
    mediaTypes: ['images'],
    allowsEditing: true,       // tizimning kesish oynasi
    aspect: [1, 1],            // kvadrat (Android da)
    quality: 0.8,
  });

  if (natija.canceled) return;           // foydalanuvchi bekor qildi
  const rasm = natija.assets[0];
  // rasm.uri, rasm.width, rasm.height, rasm.fileSize, rasm.mimeType
  setRasm(rasm.uri);
}

[!INFO] Zamonaviy Android va iOS da galereyadan tanlash uchun ko'pincha ruxsat kerak emas: tizim o'z "tanlash oynasi" ni ko'rsatadi va ilova faqat tanlangan rasmni oladi, butun galereyani emas. Bu foydalanuvchi maxfiyligi uchun yaxshi.

Kamera orqali

async function suratgaOl() {
  const { granted } = await ImagePicker.requestCameraPermissionsAsync();
  if (!granted) return Alert.alert('Kamera ruxsati kerak');

  const natija = await ImagePicker.launchCameraAsync({ allowsEditing: true, aspect: [1, 1], quality: 0.8 });
  if (!natija.canceled) setRasm(natija.assets[0].uri);
}

39-darsdagi CameraView — o'z dizayningizdagi kamera uchun. Oddiy "rasm olish" uchun launchCameraAsync — tizim kamerasi, kamroq kod.

Tanlov oynasi

function rasmManbai() {
  Alert.alert('Profil rasmi', undefined, [
    { text: 'Galereyadan tanlash', onPress: galereyadanTanla },
    { text: 'Suratga olish', onPress: suratgaOl },
    { text: 'Bekor qilish', style: 'cancel' },
  ]);
}

Bir nechta rasm

const natija = await ImagePicker.launchImageLibraryAsync({
  mediaTypes: ['images'],
  allowsMultipleSelection: true,
  selectionLimit: 5,
});

Mahsulot e'loni uchun 5 tagacha rasm.


Kichraytirish — yuklashdan oldin

import { ImageManipulator, SaveFormat } from 'expo-image-manipulator';

async function tayyorla(uri: string) {
  const kontekst = ImageManipulator.manipulate(uri);
  kontekst.resize({ width: 800 });                      // balandlik nisbatda
  const rasm = await kontekst.renderAsync();
  const natija = await rasm.saveAsync({ compress: 0.7, format: SaveFormat.JPEG });
  return natija.uri;
}
Asl Kichraytirilgan
O'lcham 4032×3024 800×600
Hajm ~6 MB ~120 KB
3G da yuklash ~40 soniya ~1 soniya

Profil uchun 400, mahsulot uchun 1200 — maqsadga qarab. JPEG — fotosuratlar uchun; PNG — faqat shaffoflik kerak bo'lsa.


Serverga yuklash

FormData bilan

async function yuklash(uri: string) {
  const forma = new FormData();
  forma.append('rasm', {
    uri,
    name: 'avatar.jpg',
    type: 'image/jpeg',
  } as any);

  const token = await tokenlar.olish();
  const javob = await fetch(`${BASE_URL}/profil/rasm`, {
    method: 'POST',
    headers: { Authorization: `Bearer ${token}` },     // ⚠️ Content-Type ni O'ZINGIZ YOZMANG
    body: forma,
  });
  if (!javob.ok) throw new Error('Yuklab bo\'lmadi');
  return javob.json();
}

React Native ning FormData si { uri, name, type } ob'ektini qabul qiladi va faylni diskdan o'zi o'qiydi.

[!WARNING] Content-Type: multipart/form-data ni qo'lda yozmang. To'g'ri sarlavhada boundary (qismlar ajratgichi) bo'lishi kerak — uni fetch o'zi qo'shadi. Qo'lda yozsangiz — boundary yo'q va server faylni topa olmaydi.

Server tomonida — PHP kursidagi 22-dars ($_FILES, MIME tekshiruv, tasodifiy nom) yoki Node.js da multer.

Progress bilan — expo-file-system

fetch yuklash progressini ko'rsatmaydi. Katta fayllar (video, ko'p rasm) uchun expo-file-system ning yuklash (upload) API sidan foydalaning: u har bir yuklangan qism haqida xabar beradi va progress chizig'ini ko'rsatish imkonini beradi. Aniq funksiya nomlari Expo SDK versiyasiga qarab o'zgarib turadi — o'z versiyangiz hujjatidan tekshiring.

Bulutga to'g'ridan-to'g'ri

Katta ilovalarda rasm serveringiz orqali emas, to'g'ridan-to'g'ri bulut omboriga (S3, Cloudflare R2) yuklanadi:

  1. Ilova serverdan imzolangan URL so'raydi (presigned URL, 5 daqiqa amal qiladi)
  2. Rasmni shu URL ga PUT qiladi
  3. Serverga "yuklandi, kalit: abc.jpg" deydi

Server trafikni o'tkazmaydi va yuklanmaydi.


To'liq misol: profil rasmi

export function ProfilRasmi() {
  const [uri, setUri] = useState<string | null>(foydalanuvchi.avatar);
  const yangila = useMutation({
    mutationFn: async (asl: string) => yuklash(await tayyorla(asl)),
    onSuccess: () => queryClient.invalidateQueries({ queryKey: ['profil'] }),
    onError: () => Alert.alert('Xato', "Rasmni yuklab bo'lmadi"),
  });

  async function tanla() {
    const n = await ImagePicker.launchImageLibraryAsync({ mediaTypes: ['images'], allowsEditing: true, aspect: [1, 1], quality: 1 });
    if (n.canceled) return;
    setUri(n.assets[0].uri);                 // darhol ko'rsatish (optimistik)
    yangila.mutate(n.assets[0].uri);
  }

  return (
    <Pressable onPress={tanla} accessibilityLabel="Profil rasmini o'zgartirish">
      <Image source={uri ? { uri } : require('@/assets/images/avatar.png')} style={{ width: 100, height: 100, borderRadius: 50 }} />
      {yangila.isPending && (
        <View style={[StyleSheet.absoluteFill, { borderRadius: 50, backgroundColor: 'rgba(0,0,0,0.4)', justifyContent: 'center' }]}>
          <ActivityIndicator color="white" />
        </View>
      )}
    </Pressable>
  );
}

Nima uchun shunday?

Nima uchun rasmni serverda emas, telefonda kichraytirish kerak?

Chunki eng qimmat resurs — tarmoq. 6 MB ni yuklash — foydalanuvchi trafigi, vaqti va batareyasi; sekin tarmoqda esa yuklash umuman uzilishi mumkin. Telefon protsessori esa rasmni bir soniyadan kamroq vaqtda kichraytiradi. Server ham 6 MB lik fayllarni qabul qilishi, saqlashi va ishlashi shart emas. Mobil dasturlashning umumiy qoidasi: tarmoq orqali faqat kerakli narsani yuboring.


Xulosa

Eslab qoling:

  • launchImageLibraryAsync — galereya; launchCameraAsync — tizim kamerasi
  • allowsEditing + aspect — kesish; canceled ni tekshiring
  • Yuklashdan oldin — expo-image-manipulator bilan kichraytiring va siqing
  • FormData + { uri, name, type }; Content-Type ni qo'lda yozmang
  • Katta fayllar — progress bilan; katta ilovalar — imzolangan URL orqali bulutga
  • Optimistik ko'rsatish + useMutation

Keyingi dars

41-dars: Joylashuv va xarita — expo-location, react-native-maps