# 文件存储（Storage）
#### 场景介绍
本章节介绍华为云RDS Supabase的文件存储（Storage）功能，涵盖控制台配置、文件管理、SDK操作及安全策略设置。
文件存储底层对接华为云OBS对象存储，提供兼容S3标准的对象存储服务，即插即用。用户无需单独购买第三方存储、自建文件存储服务器，也无需承担底层运维。OBS对象存储按需计费，费用依据存储容量、请求次数与公网流量等维度产生，详见[华为云OBS计费说明](https://support.huaweicloud.com/price-obs/obs_42_0001.html)。
RDS Supabase提供一站式托管，文件上传、下载、查看由OBS桶承载，并通过灵活安全的API与SDK完成读写。
支持大文件高性能上传下载，结合Auth的行级安全（RLS）策略，可对存储桶实现精细的文件访问权限控制，以一条策略界定"谁能看、谁能传、谁能删"。
 #### 相关概念
表1概念说明 
| 概念           | 说明                                               |
|:---|:---|
| 存储桶（Bucket） | 存储文件的顶层容器，可设为公开或私有。每个存储桶对应华为云OBS桶中的一个子文件夹（子目录）。 |
| 文件夹            | 存储桶内用于组织文件的层级结构。                                 |
| 文件            | 图片、文档等二进制资源，存放于OBS对象存储，避免大文件占用数据库。              |
| 后端存储        | 文件存储功能底层对接华为云OBS对象存储，文件上传、下载、查看等均由OBS桶承载。        |
   
**存储桶与OBS桶的映射关系**：
1个华为云OBS桶承载1个RDS Supabase实例的全部存储，每个Supabase存储桶对应OBS桶下的1个子目录。
下面以电商场景的两个存储桶为例：product-images（公开，存放产品图片）与product-specs（私有，存放产品规格文档）。后续控制台操作、SDK示例与安全策略均围绕这两个桶展开。可对照本节映射关系理解各操作作用于OBS桶下的哪个子目录。
```
OBS桶（华为云）
├── product-images/   ← 公开存储桶（产品图片）
└── product-specs/    ← 私有存储桶（产品规格文档）
```
**文件类型与大小约束**：
Storage支持上传任意类型的二进制文件（图片、文档、音视频、压缩包等），可按存储桶通过MIME类型限制允许的文件类型。
单文件大小上限可按存储桶配置，超大文件建议分片上传。
#### 前提条件
表2前提条件 
| 条件          | 说明                                                |
|:---|:---|
| 华为云实名认证     | 使用存储功能前必须完成，否则文件存储功能底层无法接入OBS对象存储服务。              |
| OBS桶与AK/SK | 在华为云OBS控制台创建OBS桶，在IAM控制台创建具有OBS权限的AK/SK（建议遵循最小权限原则）。 |
| 网络可达       | 客户端需能访问Supabase应用的访问IP与访问端口。                        |
| 同区域       | OBS桶建议与RDS Supabase实例处于同一区域。                       |
   
#### 功能启用
文件存储功能底层对接华为云OBS，使用前需在华为云管理控制台完成存储功能启用配置。简要流程：
- 在华为云OBS控制台创建OBS桶，并在IAM控制台创建具有OBS权限的AK/SK。
- 在RDS Supabase控制台应用详情页的**基础信息**区域填入OBS桶名、AK、SK并启用。
详细操作步骤请参见[开启或关闭Storage存储配置](https://support.huaweicloud.com/usermanual-rds-pg/rds_supabase_12.html)。
![](https://support.huaweicloud.com/usermanual-rds-pg/public_sys-resources/notice_3.0-zh-cn.png)
经文件存储功能托管（创建）的文件及目录，业务使用期间不得直连OBS桶进行修改、删除等管理操作，须通过Supabase Storage API完成，避免Supabase侧文件元数据不一致。
#### 通过控制台管理文件
1. **创建存储桶**
   在控制台创建存储桶，相当于在OBS桶下新建一个子目录（详见[相关概念]中的映射关系），用于按业务隔离存放文件。
   1. [登录Supabase Dashboard控制台](https://support.huaweicloud.com/usermanual-rds-pg/rds_supabase_04.html)。
   
   2. 在左侧导航栏，选择"Storage"，单击"New bucket"。
      图1Supabase Dashboard创建存储桶Bucket指引   
      ![](https://support.huaweicloud.com/usermanual-rds-pg/zh-cn_image_0000002800085505.png "点击放大") 
   
   3. 在创建存储桶页面，填写相关配置项。
      图2Supabase Dashboard创建存储桶Bucket配置项界面   
      ![](https://support.huaweicloud.com/usermanual-rds-pg/zh-cn_image_0000002770885676.png "点击放大")
      - Bucket name：输入业务桶名称（对应OBS桶下的子目录名）。
      
      - Public bucket配置示例：
        - product-images（存放产品图片）：开启"Public bucket"开关。
        
        - product-specs（存放产品规格文档）：不开启"Public bucket"开关。
         
      
      - 文件大小限制和MIME类型配置示例：
        - product-images：文件大小限制10MB，允许MIME类型image/png, image/jpeg, image/webp, image/gif。
        
        - product-specs：文件大小限制50MB，允许MIME类型application/pdf。
         
       
   
   4. 单击"Create"完成创建。
      图3Bucket桶列表   
      ![](https://support.huaweicloud.com/usermanual-rds-pg/zh-cn_image_0000002800045601.png "点击放大") 
    
2. **上传和** **管理文件**
   1. 单击目标存储桶，进入详情页。
   
   2. 单击"Create folder"，创建文件存储子目录。
      图4Supabase Dashboard创建存储桶文件夹入口   
      ![](https://support.huaweicloud.com/usermanual-rds-pg/zh-cn_image_0000002770725718.png "点击放大") 
   
   3. 单击"Upload files"，上传文件。
      图5Supabase Dashboard上传文件   
      ![](https://support.huaweicloud.com/usermanual-rds-pg/zh-cn_image_0000002800085507.png "点击放大") 
   
   4. 在文件右侧菜单，可进行下载、获取URL、移动、删除等操作。
    
 
#### 安全策略设置
Storage Policies基于PostgreSQL的行级安全（RLS）机制。storage schema的RLS需处于启用状态，策略才能生效。
- [通过控制台设置]
  1. [登录Supabase Dashboard控制台](https://support.huaweicloud.com/usermanual-rds-pg/rds_supabase_04.html)。
  
  2. 在左侧导航栏，选择"Storage"，进入存储管理页面，再单击"Policies"页签。
     图6Supabase Dashboard存储桶安全策略配置列表   
     ![](https://support.huaweicloud.com/usermanual-rds-pg/zh-cn_image_0000002770885678.png "点击放大") 
  
  3. 选择要操作的存储桶Bucket，单击"New Policy"，选择使用模板或自定义。
     图7Supabase Dashboard存储桶新增自定义安全策略   
     ![](https://support.huaweicloud.com/usermanual-rds-pg/zh-cn_image_0000002800045603.png "点击放大") 
  
  4. 配置策略名称、允许操作、目标角色。 策略定义示例：
     - product-images（公开桶，允许公共读取）：
       - 策略名称：Public read for product-images
       
       - 操作类型：允许操作SELECT
       
       - 目标角色：anon
       
       - 策略定义：bucket_id = 'product-images'
        
     
     - product-specs（私有桶，仅认证用户访问自己的文件夹）：
       - 策略名称：Authenticated access for product-specs
       
       - 操作类型：允许操作INSERT、SELECT
       
       - 目标角色：authenticated
       
       - 策略定义：bucket_id = 'product-specs' AND auth.uid()::text = (storage.foldername(name))\[1\]
        
      
  
  5. 单击"Save policy"。
   
- [通过SQL语句设置]
  - 允许对指定存储桶的公共读取
    示例场景：为商品图片桶product-images设置公共读取，任何人（含未登录访客）均可通过URL直接查看产品图片，适用于官网、商品详情页等公开展示场景。
    ```
    CREATE POLICY "Public read for product-images"
    ON storage.objects FOR SELECT
    USING ( bucket_id = 'product-images' );
    ```
    
  
  - 仅允许认证用户访问自己的文件夹
    示例场景：为产品规格文档桶product-specs设置用户隔离，登录用户只能读写以自己用户ID命名的文件夹，看不到也改不了他人的规格文档，适用于各用户私密文档存放。
    ```
    -- 上传：仅在自己的文件夹内
    CREATE POLICY "Authenticated upload in user folder"
    ON storage.objects FOR INSERT
    TO authenticated
    WITH CHECK(bucket_id = 'product-specs' AND auth.uid()::text = (storage.foldername(name))[1]);
    -- 读取：仅在自己的文件夹内
    CREATE POLICY "Authenticated read in user folder"
    ON storage.objects FOR SELECT
    TO authenticated
    USING(bucket_id = 'product-specs' AND auth.uid()::text = (storage.foldername(name))[1]);
    ```
    以上示例假设存储桶内以用户ID（**auth.uid()**）作为顶层文件夹名，按文件夹隔离访问。
    
   
#### 通过SDK操作文件
通过SDK操作前请确保目标存储桶已配置相应安全策略，公开存储桶的读取无需策略。
以下操作以product-images（公开，存放产品图片）和product-specs（私有，存放产品规格文档）两个存储桶为例。
- **创建存储桶**
  示例场景：用代码创建存储桶，等于在OBS桶下新建一个按业务隔离的子目录。电商场景分别建公开的产品图片桶与私有的产品规格文档桶，使图片可被公开浏览、规格文档仅限授权访问。
  ```
  // 创建公开存储桶
  await supabase.storage.createBucket('product-images', { public: true })
  // 创建私有存储桶
  await supabase.storage.createBucket('product-specs', { public: false })
  ```
  
- **上传文件**
  示例场景：将本地文件上传到指定桶路径。运营在后台为新品上传图片时，前端选择图片上传至product-images桶，成功后图片即落入OBS，可供商品详情页直接展示。
  ```
  const file = event.target.files[0]
  const { data, error } = await supabase.storage
    .from('product-images')
    .upload('ecs/console.png', file, {
      cacheControl: '3600',
      upsert: false,
    })
  if (error) {
    console.error('上传失败', error)
    return
  }
  ```
  
- **下载文件**
  示例场景：从桶中取回文件内容。需要在前端预览或处理文件时（如客服查看用户规格文档），调用下载拿到文件数据并转为可用URL，供页面渲染或进一步处理。
  ```
  const { data, error } = await supabase.storage
    .from('product-images')
    .download('ecs/console.png')
  // data是Blob对象
  const url = URL.createObjectURL(data)
  ```
  
- **获取公开URL/签名URL**
  示例场景：取得文件的访问链接。公开桶图片用公开URL直接嵌入页面展示；私有桶敏感文档生成限时签名URL，既允许指定对象临时查看，又不长期暴露文件地址。
  ```
  // 公开存储桶 — 直接获取URL
  const { data } = supabase.storage
    .from('product-images')
    .getPublicUrl('ecs/console.png')
  // URL格式：https://{访问IP}:{访问端口}/storage/v1/object/public/product-images/ecs/console.png
  // 访问IP、访问端口请在准备工作章节获取
  // 私有存储桶 — 生成临时签名URL（60秒有效）
  const { data, error } = await supabase.storage
    .from('product-specs')
    .createSignedUrl('obs/pricing.pdf', 60)
  ```
  getPublicUrl返回的URL仅在存储桶设为public时可直接访问；私有存储桶需通过createSignedUrl生成临时签名URL。
  
- **列出文件**
  示例场景：列出某桶某目录下的文件清单。后台管理商品图片库时，分页列出某产品目录下全部图片，供运营浏览、挑选、管理。
  ```
  const { data, error } = await supabase.storage
    .from('product-images')
    .list('ecs', {
      limit: 100,
      sortBy: { column: 'name', order: 'asc' }
    })
  ```
  
- **删除文件**
  示例场景：从桶中移除文件。产品下架或图片替换时删除旧图，释放空间并避免脏数据残留；支持一次删除多个文件。
  ```
  await supabase.storage
    .from('product-images')
    .remove(['ecs/console.png', 'ecs/detail.png'])
  ```
  
 
#### 参考文档
以下为Supabase开源相关资料，仅供参考。
- [创建存储桶](https://supabase.com/docs/reference/javascript/storage-createbucket)
- [上传文件](https://supabase.com/docs/reference/javascript/storage-from-upload)
- [下载文件](https://supabase.com/docs/reference/javascript/storage-from-download)
- [获取公开URL](https://supabase.com/docs/reference/javascript/storage-from-getpublicurl)
- [创建签名URL](https://supabase.com/docs/reference/javascript/storage-from-createsignedurl)
- [列出文件](https://supabase.com/docs/reference/javascript/storage-from-list)
- [删除文件](https://supabase.com/docs/reference/javascript/storage-from-remove)
- [Storage安全策略](https://supabase.com/docs/guides/database/postgres/row-level-security)
 
