You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Native Expo+Clerk认证上传Supabase存储失败及RLS配置疑问

问题解决:Expo图片上传Supabase失败 + Clerk认证下的存储桶RLS配置

一、解决「No content provided」上传失败问题

1. 修复本地文件读取逻辑(核心问题)

React Native的fetch对file://协议的本地文件支持不稳定,易导致获取的Blob为空。改用expo-file-system读取文件内容并转换为Blob:

首先安装依赖:

npx expo install expo-file-system

导入模块并修改uploadMedia函数:

import * as FileSystem from 'expo-file-system';

const uploadMedia = async (
  uri: string,
  mediaType: 'images' | 'videos',
  supabaseClient: SupabaseClient,
  userId: string // 新增用户ID,用于RLS验证
): Promise<string | null> => {
  try {
    const extension = uri.split('.').pop() || 'jpg';
    const filename = `${uuidv4()}.${extension}`;
    // 路径加入用户ID,方便后续RLS规则校验
    const path = `${mediaType}/${userId}/${filename}`;

    // 验证文件存在性
    const fileInfo = await FileSystem.getInfoAsync(uri);
    if (!fileInfo.exists) throw new Error('文件不存在');

    // 读取文件为base64并转换为Blob
    const base64 = await FileSystem.readAsStringAsync(uri, { encoding: 'base64' });
    const mimeType = mediaType === 'images' 
      ? `image/${extension === 'png' ? 'png' : 'jpeg'}` 
      : 'video/mp4';
    const blob = new Blob([Buffer.from(base64, 'base64')], { type: mimeType });

    if (blob.size === 0) throw new Error('生成的Blob为空');
    console.log('上传Blob信息:', { size: blob.size, type: blob.type });

    // 上传时附加用户元数据(可选,配合RLS)
    const { error } = await supabaseClient.storage
      .from('food-listings-media')
      .upload(path, blob, {
        metadata: { user_id: userId }
      });

    if (error) throw error;

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

    if (!publicUrl) throw new Error('获取公网URL失败');

    return publicUrl;
  } catch (error) {
    console.error('上传媒体错误:', error);
    return null;
  }
};

2. 修正URI处理逻辑

expo-image-manipulator.manipulateAsync返回的URI已是有效的file://格式,无需手动重复添加。调用uploadMedia时传入用户ID:

const imageUrl = await uploadMedia(compressedImage.uri, 'images', supabaseClient, user.id);

额外提示

确保handleSubmit中使用的变量已从listing正确解构:

const { images, videos, title, description, selectedCuisine, maxGuests, pricePerMeal, location, rules, availability } = listing;

二、Clerk认证下的Supabase存储桶RLS配置

1. 前置关联配置

在Supabase控制台完成以下操作:

  • 进入Settings > Auth > JWT,在External OAuth Providers中添加Clerk的JWKS URL(可在Clerk Dashboard的API Keys页面获取),确保Supabase能验证Clerk签发的JWT。
  • 启用存储桶RLS:进入Storage > Buckets > food-listings-media > Edit,勾选Enable Row Level Security。

2. 创建RLS规则

在Supabase SQL编辑器中执行以下规则:

(1)允许认证用户上传自有媒体

CREATE POLICY "认证用户可上传自有媒体" ON storage.objects
FOR INSERT TO authenticated
WITH CHECK (
  bucket_id = 'food-listings-media' AND
  -- 验证JWT用户ID与路径中的用户ID一致
  auth.jwt() ->> 'sub' = split(path, '/')[1]
);

(2)允许认证用户查看自有媒体

CREATE POLICY "认证用户可查看自有媒体" ON storage.objects
FOR SELECT TO authenticated
USING (
  bucket_id = 'food-listings-media' AND
  auth.jwt() ->> 'sub' = split(path, '/')[1]
);

(3)允许匿名用户查看公开媒体(可选)

CREATE POLICY "匿名用户可查看公开媒体" ON storage.objects
FOR SELECT TO anon
USING (
  bucket_id = 'food-listings-media'
);

(4)允许认证用户删除自有媒体(可选)

CREATE POLICY "认证用户可删除自有媒体" ON storage.objects
FOR DELETE TO authenticated
USING (
  bucket_id = 'food-listings-media' AND
  auth.jwt() ->> 'sub' = split(path, '/')[1]
);

内容的提问来源于stack exchange,提问作者asim san

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 16:50:57