2

I'm trying to upload images to Supabase storage from a React Native Expo app. The images are first compressed using expo-image-manipulator, but the upload fails with:

ERROR Error uploading images: No content provided ERROR Upload Media Error: {"error": "InvalidRequest", "message": "No content provided", "statusCode": "400"}

I am also not sure how to properly configure the RLS for Supabase storage bucket as I am using authentication from clerk.

import React, { useEffect, useState } from 'react';
import {
  View,
  Text,
  StyleSheet,
  Image,
  TouchableOpacity,
  ScrollView,
  Alert,
  ActivityIndicator,
} from 'react-native';
import { useRouter, useLocalSearchParams } from 'expo-router';
import * as Location from 'expo-location';
import { createClient, SupabaseClient } from '@supabase/supabase-js';
import * as ImageManipulator from 'expo-image-manipulator';
import { v4 as uuidv4 } from 'uuid';
import { useAuth, useUser } from '@clerk/clerk-expo';
import { supabaseUrl, supabaseAnonKey } from '@/config/supabaseClient';
import { jwtDecode } from 'jwt-decode'; // Corrected import

// Define the uploadMedia function
const uploadMedia = async (
  uri: string,
  mediaType: 'images' | 'videos',
  supabaseClient: SupabaseClient
): Promise<string | null> => {
  try {
    const extension = uri.split('.').pop();
    const filename = `${uuidv4()}.${extension}`;
    const path = `${mediaType}/${filename}`;

    // For local files on device, we need to prepend 'file://'
    const formattedUri = uri.startsWith('file://') ? uri : `file://${uri}`;
    
    const response = await fetch(formattedUri);
    if (!response.ok) throw new Error(`Failed to fetch media: ${response.status}`);
    
    const blob = await response.blob();
    if (blob.size === 0) throw new Error('Blob is empty');

    console.log('Uploading blob:', { size: blob.size, type: blob.type });

    const { error } = await supabaseClient.storage
      .from('food-listings-media')
      .upload(path, blob);

    if (error) throw error;

    const { data: { publicUrl } } = supabaseClient.storage
      .from('food-listings-media')
      .getPublicUrl(path);

    if (!publicUrl) throw new Error('Failed to get public URL');

    return publicUrl;
  } catch (error) {
    console.error('Upload Media Error:', error);
    return null;
  }
};

export default function Summary() {
  const router = useRouter();
  const { newListing } = useLocalSearchParams();
  const listing = typeof newListing === 'string' ? JSON.parse(newListing) : newListing;

  const [address, setAddress] = useState<string | null>(null);
  const [errorMsg, setErrorMsg] = useState<string | null>(null);
  const [loading, setLoading] = useState<boolean>(false);
  const { getToken } = useAuth();
  const { user } = useUser(); // Get the user from Clerk

  

  useEffect(() => {
   

  // Create the Supabase client with Clerk token
  const createClerkSupabaseClient = () => {
    return createClient(supabaseUrl, supabaseAnonKey, {
      global: {
        fetch: async (url, options = {}) => {
          const token = await getToken({ template: 'supabase' });
          if (token) {
            const decodedToken = jwtDecode(token);
            console.log('User Subject (sub) from JWT:', decodedToken.sub); // Log the subject status
          } else {
            console.log('No token found for the user.');
          }

          const headers = new Headers(options?.headers);
          headers.set('Authorization', `Bearer ${token}`);

          return fetch(url, {
            ...options,
            headers,
          });
        },
      },
    });
  };

  const handleSubmit = async () => {
    try {
      setLoading(true);
      const supabaseClient = createClerkSupabaseClient();

      // Upload images
      const uploadedImages = await Promise.all(
        images.map(async (imageUri: string) => {
          const compressedImage = await ImageManipulator.manipulateAsync(
            imageUri,
            [{ resize: { width: 800 } }],
            { compress: 0.7, format: ImageManipulator.SaveFormat.JPEG }
          );
          const imageUrl = await uploadMedia(compressedImage.uri, 'images', supabaseClient);
          if (!imageUrl) throw new Error('Image upload failed');
          return imageUrl;
        })
      );

      // Upload videos
      const uploadedVideos = await Promise.all(
        videos.map(async (videoUri: string) => {
          const videoUrl = await uploadMedia(videoUri, 'videos', supabaseClient);
          if (!videoUrl) throw new Error('Video upload failed');
          return videoUrl;
        })
      );

      // Prepare listing data with uploaded media URLs
      const listingData = {
        user_id: user?.id, // Use the original Clerk user ID here
        title,
        description,
        cuisine_type: selectedCuisine,
        is_vegan: false, // Set based on your app's logic
        is_vegetarian: false, // Set based on your app's logic
        max_guests: parseInt(maxGuests, 10),
        price_per_meal: parseFloat(pricePerMeal),
        discount_for_groups: null, // Set as needed
        location: location
          ? { latitude: location.latitude, longitude: location.longitude }
          : null,
        rules,
        availability,
        images: uploadedImages,
        videos: uploadedVideos,
        gifs: null, // Set as needed
      };

      console.log('Listing Data to Insert:', listingData);

      // Insert the data into the listings table
      const { error: insertError } = await supabaseClient
        .from('listings')
        .insert([listingData]);

      if (insertError) {
        console.error('Error inserting data:', insertError);
        Alert.alert('Error', 'Failed to submit the listing. Please try again.');
      } else {
        Alert.alert('Success', 'Your listing has been added successfully!');
        router.push({ pathname: './(tabs)/index' }); // Updated to use correct path
      }
    } catch (error: unknown) {
      const errorMessage =
        error instanceof Error
          ? error.message
          : 'An unexpected error occurred. Please try again.';
      Alert.alert('Error', errorMessage);
      console.error('Submission error:', error);
    } finally {
      setLoading(false);
    }
  };

  
2
  • 1
    Were you able to fix this? Commented Dec 18, 2024 at 5:51
  • getting same error Commented Jan 13 at 10:26

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.