如何使用 @supabase/supabase-js 将图像上传到 React Native 应用程序中的 Supabase 存储?身体

问题描述 投票:0回答:1

我正在使用 Expo 和 Supabase 开发 React Native 应用程序。我需要将图像上传到 Supabase 存储桶。我正在使用 Expo 的 ImagePicker 来选择图像,并且我想使用 @supabase/supabase-js 库将这些图像直接上传到 Supabase Storage。

尽管遵循了各种教程和示例,但图像并未上传到存储桶,并且我没有收到任何具体的错误消息来调试问题。在我的手机上选择图像并单击“选择”后,没有任何反应 - 图像没有上传,也没有显示错误。

我已确保我的 Supabase 存储桶策略允许公开上传和访问。

这是上传功能的当前代码:

import React, { useState } from 'react';
import {
  View,
  Text,
  TextInput,
  TouchableOpacity,
  StyleSheet,
  SafeAreaView,
  ScrollView,
  Image,
  Alert,
} from 'react-native';
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
import { useNavigation, useRoute } from '@react-navigation/native';
import * as ImagePicker from 'expo-image-picker';
import { supabase } from '../database/supabaseClient';
import 'react-native-url-polyfill/auto';
import { Upload } from '@supabase/supabase-js';

const UploadSurpriseBagScreen = () => {
  const navigation = useNavigation();
  const route = useRoute();
  const { userId } = route.params || {};

  const [name, setName] = useState('');
  const [bagNumber, setBagNumber] = useState('');
  const [pickupHour, setPickupHour] = useState('');
  const [validation, setValidation] = useState('');
  const [price, setPrice] = useState('');
  const [description, setDescription] = useState('');
  const [category, setCategory] = useState('Breakfast');
  const [imageUri, setImageUri] = useState('');
  const [uploading, setUploading] = useState(false);

  const pickImage = async () => {
    const permissionResult = await ImagePicker.requestMediaLibraryPermissionsAsync();

    if (permissionResult.granted === false) {
      alert("You've refused to allow this app to access your photos!");
      return;
    }

    const pickerResult = await ImagePicker.launchImageLibraryAsync({
      allowsEditing: true,
      aspect: [4, 3],
      quality: 1,
    });

    if (!pickerResult.cancelled) {
      setImageUri(pickerResult.uri);
      return pickerResult;
    }
  };

  const uploadImage = async (uri) => {
    try {
      setUploading(true);
      const response = await fetch(uri);
      const blob = await response.blob();
      const fileExt = uri.split('.').pop();
      const fileName = `${Date.now()}.${fileExt}`;
      const filePath = `${fileName}`;

      const { data, error } = await supabase.storage.from('surprise_bags').upload(filePath, blob);

      if (error) {
        throw error;
      }

      const { publicURL, error: publicURLError } = await supabase.storage.from('surprise_bags').getPublicUrl(filePath);

      if (publicURLError) {
        throw publicURLError;
      }

      return publicURL;
    } catch (error) {
      console.error('Error uploading image:', error);
      alert('Error uploading image');
      return null;
    } finally {
      setUploading(false);
    }
  };

  const handleUploadBag = async () => {
    if (!name || !bagNumber || !pickupHour || !validation || !price || !description || !imageUri) {
      alert('Please fill out all fields and upload an image');
      return;
    }

    const imageUrl = await uploadImage(imageUri);

    if (!imageUrl) return;

    const { data: shop, error: shopError } = await supabase
      .from('shops')
      .select('id')
      .eq('employee_id', userId)
      .single();

    if (shopError) {
      console.error('Error fetching shop:', shopError);
      alert('Error fetching shop information');
      return;
    }

    const { error } = await supabase
      .from('surprise_bags')
      .insert([
        {
          employee_id: userId,
          shop_id: shop.id,
          name,
          bag_number: bagNumber,
          pickup_hour: pickupHour,
          validation,
          price,
          description,
          category,
          image_url: imageUrl,
        },
      ]);

    if (error) {
      console.error('Error uploading surprise bag:', error);
      alert('Error uploading surprise bag');
    } else {
      alert('Surprise bag uploaded successfully');
      navigation.goBack();
    }
  };

  return (
    <SafeAreaView style={styles.container}>
      <TouchableOpacity onPress={() => navigation.goBack()} style={styles.backButton}>
        <Icon name="arrow-left" size={24} color="#000" />
      </TouchableOpacity>
      <ScrollView contentContainerStyle={styles.scrollContainer}>
        <Text style={styles.headerTitle}>Upload Surprise Bags</Text>
        <TouchableOpacity style={styles.uploadPhotoContainer} onPress={async () => {
          const response = await pickImage();
          if (response?.uri) {
            setImageUri(response.uri);
          }
        }}>
          {imageUri ? (
            <Image source={{ uri: imageUri }} style={styles.uploadedImage} />
          ) : (
            <>
              <Icon name="image-outline" size={50} color="#82866b" />
              <Text style={styles.uploadPhotoText}>Upload Photo</Text>
            </>
          )}
        </TouchableOpacity>

        <Text style={styles.label}>Name</Text>
        <TextInput
          style={styles.input}
          placeholder="e.g. Surprise Bag"
          value={name}
          onChangeText={setName}
        />

        <Text style={styles.label}>Bag no.</Text>
        <TextInput
          style={styles.input}
          placeholder="e.g. #001"
          value={bagNumber}
          onChangeText={setBagNumber}
        />

        <Text style={styles.label}>Pick up Hour</Text>
        <TextInput
          style={styles.input}
          placeholder="e.g. 12:30pm - 4:30am"
          value={pickupHour}
          onChangeText={setPickupHour}
        />

        <Text style={styles.label}>Validation</Text>
        <TextInput
          style={styles.input}
          placeholder="e.g. 07/02/24 - 09/02/24"
          value={validation}
          onChangeText={setValidation}
        />

        <Text style={styles.label}>Price</Text>
        <TextInput
          style={styles.input}
          placeholder="e.g. $3.50"
          value={price}
          onChangeText={setPrice}
        />

        <Text style={styles.label}>What you could get</Text>
        <TextInput
          style={styles.input}
          placeholder="e.g. Lorem ipsum dolor sit amet consectetur."
          value={description}
          onChangeText={setDescription}
        />

        <Text style={styles.label}>Food Category</Text>
        <View style={styles.pickerContainer}>
          <TouchableOpacity
            onPress={() => setCategory('Breakfast')}
            style={[styles.pickerButton, category === 'Breakfast' && styles.pickerButtonSelected]}
          >
            <Text style={styles.pickerButtonText}>Breakfast</Text>
          </TouchableOpacity>
          <TouchableOpacity
            onPress={() => setCategory('Lunch')}
            style={[styles.pickerButton, category === 'Lunch' && styles.pickerButtonSelected]}
          >
            <Text style={styles.pickerButtonText}>Lunch</Text>
          </TouchableOpacity>
          <TouchableOpacity
            onPress={() => setCategory('Dinner')}
            style={[styles.pickerButton, category === 'Dinner' && styles.pickerButtonSelected]}
          >
            <Text style={styles.pickerButtonText}>Dinner</Text>
          </TouchableOpacity>
        </View>

        <TouchableOpacity style={styles.uploadButton} onPress={handleUploadBag} disabled={uploading}>
          <Text style={styles.uploadButtonText}>{uploading ? 'Uploading...' : 'Upload Bag'}</Text>
        </TouchableOpacity>
      </ScrollView>
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#FFFFFF',
  },
  backButton: {
    marginTop: 10,
    marginLeft: 10,
  },
  scrollContainer: {
    paddingHorizontal: 20,
  },
  headerTitle: {
    fontSize: 26,
    fontWeight: 'bold',
    textAlign: 'center',
    marginTop: 30,
    marginBottom: 20,
  },
  uploadPhotoContainer: {
    borderColor: '#676a61',
    borderWidth: 1,
    borderStyle: 'dashed',
    borderRadius: 10,
    padding: 20,
    alignItems: 'center',
    marginBottom: 20,
  },
  uploadedImage: {
    width: 100,
    height: 100,
    borderRadius: 10,
  },
    uploadPhotoText: {
    fontSize: 16,
    color: '#5c5f4c',
    textAlign: 'center',
    marginVertical: 10,
  },
  label: {
    fontSize: 16,
    fontWeight: 'bold',
    marginBottom: 5,
    color: '#5c5f4c',
  },
  input: {
    borderWidth: 1,
    borderColor: '#000',
    paddingVertical: 8,
    paddingHorizontal: 10,
    borderRadius: 5,
    marginBottom: 20,
    height: 50,
  },
  pickerContainer: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    marginBottom: 20,
  },
  pickerButton: {
    flex: 1,
    paddingVertical: 10,
    alignItems: 'center',
    borderWidth: 1,
    borderColor: '#000',
    borderRadius: 5,
    marginHorizontal: 5,
    backgroundColor: '#fff',
  },
  pickerButtonSelected: {
    backgroundColor: '#82866b',
  },
  pickerButtonText: {
    color: '#5c5f4c',
  },
  uploadButton: {
    backgroundColor: '#82866b',
    paddingVertical: 12,
    borderRadius: 5,
    alignItems: 'center',
    marginBottom: 20,
  },
  uploadButtonText: {
    color: '#fff',
    fontSize: 18,
    fontWeight: 'bold',
  },
});

export default UploadSurpriseBagScreen;


我使用了以下政策:

CREATE POLICY "Anyone can upload to surprise_bags" 
ON storage.objects
FOR INSERT
WITH CHECK (bucket_id = 'surprise_bags');

CREATE POLICY "Anyone can update surprise_bags" 
ON storage.objects
FOR UPDATE
USING (bucket_id = 'surprise_bags')
WITH CHECK (bucket_id = 'surprise_bags');

CREATE POLICY "Anyone can view surprise_bags" 
ON storage.objects
FOR SELECT
USING (bucket_id = 'surprise_bags');

javascript react-native file-upload expo supabase
1个回答
0
投票

如果您阅读了supabase文档这里 您不能使用 blob 在 React Native 中上传。您必须从 base64 文件数据上传

ArrayBuffer
。 你可以这样做: 使用 expo 的示例

import * as FileSystem from "expo-file-system";
import { decode } from "base64-arraybuffer";

       // Read the file as a Base64-encoded string using Expo's FileSystem
        const base64 = await FileSystem.readAsStringAsync(uri, {
            encoding: FileSystem.EncodingType.Base64,
        });
        // Decode the Base64 string to an ArrayBuffer
        const arrayBuffer = decode(base64);

        // Upload the image to Supabase Storage
        const { data, error } = await supabase.storage
            .from(bucketName) // Replace with your storage bucket name
            .upload(fileName, arrayBuffer, {
                upsert: true,
                contentType: "image/*",
            });

© www.soinside.com 2019 - 2024. All rights reserved.