Гид по технологиям

Загрузка и показ изображений в Supabase из Next.js

7 min read Backend Обновлено 18 Dec 2025
Загрузка и показ изображений в Supabase и Next.js
Загрузка и показ изображений в Supabase и Next.js

Три пронумерованных контейнера для хранения с синими рольставнями

К чему приведёт это руководство

  • Научитесь создавать проект Supabase и storage bucket.
  • Настроите supabase-js в Next.js и environment-переменные.
  • Реализуете форму загрузки файла и загрузку в bucket с уникальным именем.
  • Получите публичный URL и выведете изображение в компоненте Image Next.js.
  • Узнаете, когда такой подход не подойдёт, и получите рекомендации по безопасности и соответствию требованиям конфиденциальности.

Important: примеры кода — на JavaScript/React. Поддерживаются Next.js 12+ и App Router в Next.js 13+, но там есть отличия в структуре страниц — в тексте указаны альтернативы.

Краткое определение терминов

  • Supabase Storage: объектное хранилище, похожее на S3, интегрированное с Supabase.
  • Bucket: контейнер для файлов внутри Supabase Storage.
  • anon key: публичный ключ клиента для браузера (ограниченные права).

Создание проекта Supabase

Если у вас ещё нет приложения Next.js, сначала создайте минимальный проект по официальному руководству Next.js.

Далее — создание проекта Supabase:

  1. Перейдите на сайт Supabase и зарегистрируйтесь или войдите в аккаунт.
  2. В панели управления нажмите Create a new project.
  3. Укажите имя проекта и нажмите Create project.
  4. После создания вы попадёте в дашборд проекта.

После создания проекта создайте storage bucket (раздел ниже). Сохраняйте URL проекта и anon key в .env-файле.

Создание хранилища (bucket) в Supabase

Bucket — это место, где хранятся ваши медиафайлы (изображения, видео). Bucket можно создать через дашборд или программно через клиент.

Через дашборд:

  1. В дашборде проекта откройте раздел Storage.
  2. Нажмите New Bucket.
  3. Укажите имя, например images, и при необходимости настройте публичный доступ.
  4. Нажмите Create Bucket.

Важно: если вы хотите, чтобы файлы были доступны публично через прямой URL без авторизации, отметьте bucket как публичный или создайте публичные политики доступа. В противном случае выдавайте временные URL (signed URL) для доступа.

Настройка клиента Supabase в приложении

Установите библиотеку supabase-js:

npm install @supabase/supabase-js

Создайте папку lib в корне проекта или внутри src и файл supabase.js. В примерах ниже используется Node-совместимая и браузерная инициализация через createClient.

.env.local (в корне проекта Next.js)

SUPABASE_PROJECT_URL="your_project_url"
SUPABASE_ANON_KEY="your_project_anon_key"

lib/supabase.js

import { createClient } from '@supabase/supabase-js'

export const supabase = createClient(
  process.env.SUPABASE_PROJECT_URL,
  process.env.SUPABASE_ANON_KEY
)

Совет: в Vercel и других платформах задайте эти переменные в настройках окружения, а не закладывайте ключи в репозиторий.

Создание страницы с формой для загрузки

Если вы используете App Router (Next.js 13+), создайте папку app/upload и файл page.js. Для Next.js 12 создайте pages/upload.js.

app/upload/page.js (пример для App Router):

"use client"
import { useState } from "react"
import { v4 as uuidv4 } from "uuid"
import { supabase } from "@/lib/supabase"

export default function Page() {
  const [file, setFile] = useState(null)
  const [uploading, setUploading] = useState(false)
  const [error, setError] = useState(null)
  const [publicUrl, setPublicUrl] = useState(null)

  const handleFileSelected = (e) => {
    setFile(e.target.files[0])
  }

  const handleSubmit = async (e) => {
    e.preventDefault()
    if (!file) return
    setUploading(true)
    setError(null)

    try {
      const filename = `${uuidv4()}-${file.name}`
      const { data, error: uploadError } = await supabase.storage
        .from('images')
        .upload(filename, file, {
          cacheControl: '3600',
          upsert: false,
        })

      if (uploadError) throw uploadError

      const filepath = data.path
      // Получение публичного URL
      const { data: urlData } = supabase.storage
        .from('images')
        .getPublicUrl(filepath)

      setPublicUrl(urlData.publicUrl)
    } catch (err) {
      setError(err.message)
    } finally {
      setUploading(false)
    }
  }

  return (
    
{error &&

{error}

} {publicUrl && (

Изображение загружено:

Загруженное изображение
)}
) }

Замечания:

  • Установите пакет uuid: npm install uuid
  • В браузере безопаснее использовать public anon key, но не используйте service_role ключ в клиентском коде.

Загрузка файла в Supabase Storage

Ключевые моменты при загрузке:

  • Создавайте уникные имена файлов (UUID + оригинальное имя) — чтобы избежать перезаписей.
  • Используйте опцию cacheControl для кеширования в CDN.
  • Управляйте upsert: false чтобы не затирать существующие файлы.

Пример загрузки (внутри handleSubmit) уже показан выше. После upload Supabase вернёт объект data с полем path.

Получение URL и вывод изображения

Два способа получить URL:

  1. Через метод getPublicUrl клиента Supabase:
const { data } = supabase
  .storage
  .from('images')
  .getPublicUrl(filepath)

// data.publicUrl — публичный URL
  1. Сконструировать URL вручную: соединить URL вашего проекта, имя bucket и путь — но лучше использовать метод клиента, он учтёт путь и префиксы.

В Next.js можно выводить изображение через компонент Image, но добавьте домен в next.config.js (если используете Image):

next.config.js

module.exports = {
  images: {
    domains: ['your-project-ref.supabase.co'],
  },
}

Или используйте обычный тег img как в примере выше.

Политики доступа и разрешения

Чтобы приложение могло загружать и читать файлы, настройте политику доступа:

  1. В дашборде Supabase откройте Storage > ваш bucket > Access.
  2. Нажмите New policy.
  3. Выберите For full customization и создайте политику, которая даёт INSERT и SELECT (если вам нужно только чтение — создайте политику только для SELECT).
  4. Проверьте роли и убедитесь, что политики не открывают конфиденциальные файлы посторонним.

Важно: вместо открытого bucket для приватных файлов используйте signed URL (временный доступ), а для публичного контента — публичный bucket.

Когда такой подход не подойдёт (counterexamples)

  • Если вам требуется транзакционная целостность между записью большого BLOB и другими операциями БД (труднее атомарно откатить и файл, и данные).
  • Если файлы очень большие и требуются chunked-upload/streaming. Supabase Storage подходит не для всех случаев стриминга больших мультимедийных файлов.
  • Когда строгие требования по хранению и шифрованию на стороне сервера не позволяют использовать публичные облачные buckets.

Альтернативные подходы

  • Хранить файлы в PostgreSQL (bytea) — подходит для очень небольших файлов, но обычно дороже по производительности.
  • Использовать S3/MinIO/Backblaze B2 напрямую, если уже есть готовая инфраструктура.
  • CDN + Signed URLs: для приватного контента создавайте временные ссылки и кешируйте через CDN.

Пошаговая методология внедрения (mini-methodology)

  1. Оцените требования: публичный/приватный контент, размер файлов, частота запросов.
  2. Создайте проект Supabase и bucket.
  3. Настройте env-переменные и клиента.
  4. Реализуйте форму загрузки и тестовую страницу.
  5. Настройте политики доступа и протестируйте операции INSERT/SELECT.
  6. Добавьте мониторинг/логирование и автоматические тесты.

Чек-лист для ролей

Разработчик:

  • Настроил supabase.js и environment-переменные.
  • Реализовал загрузку и получение public URL.
  • Обработал ошибки и показывается прогресс загрузки.

DevOps:

  • Проверил переменные окружения на staging/production.
  • Настроил domains в next.config.js (если используете Image).
  • Настроил бэкап и политику хранения.

Безопасность:

  • Убедился, что service_role ключ не в клиентском коде.
  • Настроил политики доступа для bucket.
  • При необходимости внедрил signed URLs и их срок действия.

Критерии приёмки

  • Форма загружает файл и возвращает public URL.
  • Изображение корректно отображается на странице.
  • В логах нет ошибок загрузки при типичных сценариях.
  • Политики доступа соответствуют требованиям безопасности.

Тестовые сценарии и приёмка

  1. Успешная загрузка изображения (jpeg/png), отобразить URL.
  2. Попытка загрузки без файла — блокировка отправки.
  3. Ошибка сети — обработка и показ сообщения.
  4. Попытка перезаписи существующего файла — проверка upsert:false.
  5. Доступ к приватному файлу без подписанного URL — отказ.

Rollback / инцидентный план

  • Если файлы стали публичными по ошибке: немедленно снять публичный доступ у bucket, регенерировать политики и при необходимости перевыпустить ссылки для пользователей.
  • Если файлы были перезаписаны: восстановить из бэкапа (если настроен бэкап). Рекомендуется хранить запись в БД с историей версий файлов.

Советы по безопасности и соответствию (GDPR/конфиденциальность)

  • Не храните персональные данные в имени файла (email, идентификаторы).
  • Для персональных изображений используйте приватный bucket и signed URL с коротким TTL.
  • Документируйте срок хранения файлов и реализуйте политику удаления (retention).
  • Если требуется шифрование at-rest, проверьте, предоставляет ли Supabase нужные опции; при необходимости используйте шифрование на уровне приложения.

Notes: ответственность за соответствие локальным законам и GDPR лежит на владельце приложения.

Советы по производительности

  • Настройте cacheControl для статичных изображений.
  • Используйте CDN перед bucket, если ожидается высокий трафик.
  • Массивные наборы миниатюризируйте изображение перед загрузкой на клиенте (resize) либо генерируйте превью на сервере.

Совместимость и миграция

  • Next.js 12 (pages) и Next.js 13+ (app) имеют разную организацию файлов — в статье показаны примеры для App Router; для pages создавайте upload.js в папке pages.
  • Существующий S3-хранилище можно оставить и подключать через собственный CDN, но перенос на Supabase потребует скриптов миграции (копирование объектов и запись путей).

Примеры дополнительных сниппетов

Получение signed URL (временная ссылка) для приватного файла:

const { data, error } = await supabase.storage
  .from('images')
  .createSignedUrl('path/to/file.jpg', 60) // TTL в секундах

if (error) throw error
const signedUrl = data.signedUrl

Удаление файла:

const { error } = await supabase.storage.from('images').remove(['path/to/file.jpg'])
if (error) console.error(error)

Список файлов в bucket:

const { data, error } = await supabase.storage.from('images').list('/', { limit: 100, offset: 0 })

Пример мини-Playbook для продакшена

  1. Настроить мониторинг ошибок загрузки и метрик (latency, error rate).
  2. Настроить резервное копирование bucket и периодическую проверку целостности.
  3. Добавить автоматические тесты для загрузки/чтения/удаления.
  4. План обновления политик доступа и уведомления при изменении.

Итог и рекомендации

Использование Supabase Storage для хранения изображений — простой и масштабируемый подход для большинства веб-приложений. Он экономичнее и удобнее, чем хранение BLOB в базе данных. Тем не менее, внимательно продумайте требования по приватности, размерам файлов и требованиям к транзакциям.

Summary:

  • Используйте уникальные имена файлов (UUID).
  • Храните ключи в переменных окружения.
  • Не используйте service_role ключи в клиентском коде.
  • Для приватных изображений отдавайте signed URL.

Если нужно, могу добавить конкретный пример API-роута Next.js для генерации временного signed URL под приватные файлы или готовый миграционный скрипт для переноса файлов из S3.

Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

Gofakeit — фейковые данные в Go
Разработка

Gofakeit — фейковые данные в Go

Расшифровка аудио в Microsoft Word (онлайн)
Руководство

Расшифровка аудио в Microsoft Word (онлайн)

Проверка блока питания ПК мультиметром
Железо

Проверка блока питания ПК мультиметром

Диктовка в Windows 10: руководство и советы
Windows

Диктовка в Windows 10: руководство и советы

Закладки в Microsoft Word — руководство
Office

Закладки в Microsoft Word — руководство

Основы классов Python — пример Car
Программирование

Основы классов Python — пример Car