Портфолио начинается с выбора файла, но на этом оно редко заканчивается. Посетители используют разные экраны и браузеры. Редакторам нужны доступные описания. Портрету может потребоваться иная обрезка, чем карточке в галерее. Видео нужен качественный плеер. И каждый байт все равно должен быстро доходить до посетителя.
В этом руководстве мы создадим Media Quest — игровую студию портфолио, которая полностью решает эту задачу. Вы будете загружать изображения и видео напрямую в Cloudinary, считывать их в серверном компоненте Next.js, изучать адаптивные трансформации и позволите Cloudinary AI Vision генерировать и сохранять замещающий текст (alt text) и подписи. Server Action обеспечивает защищенное автоматическое заполнение метаданных ИИ для старых изображений. Главная страница здесь — это рабочий продукт, а не просто маркетинговая страница.
Главное доказательство — сохранение состояния после обновления: загрузите ресурс, обновите браузер, и тот же ресурс вернется через Cloudinary Search API. Это не просто локальный массив, оформленный как галерея.
Media Quest включает четыре взаимосвязанные станции:
- Upload Mission открывает Cloudinary Upload Widget с источниками из локальных файлов, камеры и удаленных URL, или создает управляемое изображение с помощью мультимодального API генерации изображений Cloudinary.
- Portfolio Vault считывает сохраненные изображения и видео из одной папки ресурсов Cloudinary, а затем добавляет фильтры поиска и типы ресурсов.
- Transformation Lab создает несколько визуальных вариантов из одного оригинала, включая автоматическую обрезку по гравитации и опциональное генеративное заполнение.
- Delivery Board объясняет адаптивный srcset, автоматический формат и качество, имя хоста доставки и кэширование CDN.
Откройте любой ресурс, чтобы изучить его размеры и URL доставки. Лист сведений также отображает автоматически созданную ИИ историю. Cloudinary AI Vision создает уникальный замещающий текст и подпись для портфолио, а приложение проверяет и сохраняет оба значения в качестве контекстных метаданных Cloudinary. Вам не нужно писать ни одно из этих полей вручную.
Браузер может выбирать файлы и показывать немедленную обратную связь. Он не должен получать секретный ключ API. Сервер отлично справляется с аутентифицированными запросами Search API и контролируемой записью метаданных, и не должен становиться ненужным прокси-сервером для медиа.
Такое разделение дает каждому уровню свою четкую задачу:
Cloudinary Upload Widget поддерживает прямые загрузки, в то время как неподписанный пресет загрузки обеспечивает ограничения на стороне аккаунта. Документация Cloudinary Upload Widget и документация по пресетам загрузки охватывают доступные источники и элементы управления пресетами.
Серверные компоненты Next.js удерживают учетные данные Admin API вне клиентского бандла. Server Action позволяет авторизованному редактору запустить автоматическое заполнение ИИ для старого ресурса, не полагаясь на метаданные, написанные автором. См. текущие Next.js Server Actions guide рекомендации по безопасности и проверке для этого фреймворка.
Вам понадобятся:
- Node.js версии 20.9 или новее
- Аккаунт Cloudinary
- Дополнение Cloudinary AI Vision для автоматического описания изображений
- Дополнение Cloudinary Image Generation для опционального пути генерации
- Базовое знание React и TypeScript
- Около 30 минут для основного пути
Клонируйте проект, установите пакеты и создайте файл локального окружения:
Проект использует Next.js 16, React 19, next-cloudinary, Cloudinary Node SDK, Zod, Tailwind CSS и shadcn/ui. Его игривая тема TweakCN использует хостинговые шрифты, скругленные карточки, ярко-желтые элементы действий и фиолетовые акценты прогресса.
Откройте консоль Cloudinary, выберите Settings, откройте Upload и добавьте пресет загрузки.
Используйте следующие значения:
- Имя пресета: media_portfolio_unsigned
- Режим подписи: Unsigned (Неподписанный)
- Папка ресурсов: media-portfolio
- Разрешенные форматы: jpg,jpeg,png,webp,avif,gif,mp4,webm,mov
Установите ограничения на размер файлов на уровне аккаунта, соответствующие вашему продукту. Демо также просит виджет разрешить не более восьми файлов в очереди, 12 МБ для изображений и 80 МБ для видео. Настройки браузера улучшают опыт, но пресет является границей принудительного исполнения.
Добавьте публичные значения загрузки в .env.local:
Эти значения могут попасть в браузер. Неподписанный пресет не является секретом, поэтому он должен быть ограниченным. Никогда не добавляйте NEXT_PUBLIC_ к секретному ключу API.
UploadMission — это клиентский компонент, потому что виджету нужны события браузера. Основная интеграция компактна:
Сам компонент выполняет проверку типов результатов, сообщает о статусе через область aria-live и добавляет временную оптимистичную карточку. Когда очередь заканчивается, router.refresh() запрашивает у серверного компонента сохраненное представление Cloudinary. Для изображений он также передает неизменяемый ID ресурса, публичный ID и версию в /api/describe. Сервер считывает ресурс обратно и требует точную папку портфолио, тег пресета, тип ресурса, соответствующую версию создания и 15-минутное окно, прежде чем AI Vision сможет потратить квоту. Приложение определяет режим папки Cloudinary. Виджет отправляет asset_folder=media-portfolio для динамических папок или folder=media-portfolio для устаревшей среды с фиксированными папками. Неподписанный пресет определяет то же место назначения в качестве авторитетного значения.
Прочитайте полную реализацию в src/components/upload-mission.tsx.
Прочитайте полную реализацию в src/components/upload-mission.tsx.
Этот путь эффективен, потому что файл перемещается от посетителя к Cloudinary. Ваша функция Next.js не буферизирует большое изображение или видео.
У Upload Mission также есть вкладка Generate. Она следует тому же шаблону управляемых ресурсов, который используется в сопутствующем проекте генерации изображений: браузер отправляет промпт, семейство моделей, уровень и токен редактирования портфолио в обработчик маршрута Node.js. Ключ API и секрет Cloudinary остаются на сервере.
Включите дополнение Cloudinary Image Generation, затем создайте подписанный пресет с именем media_portfolio_generations. Установите его динамическую папку ресурсов или устаревшую папку на то же значение media-portfolio, которое используется пресетом неподписанного Upload Widget.
Добавьте имя пресета в .env.local:
Маршрут сопоставляет пять разрешенных семейств моделей и стандартные или премиальные уровни с текущими ID моделей. Он вызывает эндпоинт text_to_image с областью действия аккаунта, используя базовую HTTP-аутентификацию:
Запрос имеет тайм-аут 120 секунд, так как генерация изображения занимает больше времени, чем запись метаданных. Токен редактирования защищает кредиты дополнения в этом демо для одного пользователя. Публичный многопользовательский продукт должен добавить аутентификацию, надежное ограничение частоты запросов, проверки владения и мониторинг квоты аккаунта.
Согласованность папок проверяется, а не предполагается. Перед расходованием квоты маршрут считывает пресет генерации через Admin API и подтверждает, что он подписан и нацелен на media-portfolio через соответствующее поле папки. После генерации он считывает неизменяемый ID ресурса и снова проверяет сохраненную папку. Галерея также выбирает asset_folder для динамических папок или folder для фиксированных папок, поэтому и загрузки, и генерации остаются видимыми после обновления. Защищенный маршрут генерации затем запрашивает у AI Vision замещающий текст и подпись перед возвратом сгенерированного ресурса.
Добавьте значения только для сервера в .env.local:
Модуль Cloudinary начинается с import "server-only". Этот защитный механизм заставляет случайный импорт на стороне клиента завершиться ошибкой во время разработки, вместо того чтобы допустить утечку привилегированного кода в браузер.
Домашняя страница является серверным компонентом:
connection() сообщает Next.js, что результат относится ко времени запроса. Динамические данные Cloudinary не фиксируются в производственной сборке.
getPortfolioSnapshot() настраивает Node SDK и отправляет запросы к Search API:
Функция рассматривает ответ SDK как внешние данные, проверяет типы полей и возвращает более компактную структуру PortfolioAsset. Это предотвращает зависимость кода рендеринга от каждой детали ответа Admin API. В актуальной документации Cloudinary Search API описаны выражения, сортировка, поля и пагинация.
При отсутствии учетных данных Media Quest переходит в специальный демонстрационный режим с публичными ресурсами из демо-облака Cloudinary. Состояния загрузки и настройки метаданных остаются видимыми. Приложение никогда не выдает эти примеры за постоянные данные пользователя.
Портфолио не создает и не загружает по пять копий каждого изображения. Оно сохраняет один оригинал и выражает каждую потребность макета через трансформацию.
Карточки в галерее используют CldImage, ширину и высоту, подсказки для адаптивных размеров, а также доступный альтернативный текст (alt). Компонент генерирует адаптивный srcset, в то время как Cloudinary применяет автоматическую доставку формата и качества. f_auto позволяет выбрать эффективный формат, поддерживаемый браузером, а q_auto сбалансированно сочетает визуальное качество и объем в байтах. Оба параметра описаны в руководстве по оптимизации изображений Cloudinary.
Transformation Lab делает модель URL наглядной:
- Fit сохраняет весь кадр целиком внутри горизонтального прямоугольника.
- Smart crop использует обрезку с заполнением и автоматическим определением центра тяжести для защиты визуально значимой области.
- Portrait сначала определяет объект, а затем создает композицию в вертикальном формате для публикации.
- Grayscale изменяет стиль отображения без создания еще одного сохраненного исходного файла.
- Generative fill расширяет изображение до нового соотношения сторон, когда пользователь явно включает эту функцию.
Генеративные трансформации могут увеличивать затраты на обработку и требовать проверки человеком. Media Quest помечает этот выбор соответствующим образом и оставляет его отключенным по умолчанию. Это более честный паттерн продукта, чем молчаливое списание кредитов на трансформацию.
Трансформации Cloudinary — это инструкции в виде URL. Различные URL трансформаций становятся отдельными кэшируемыми производными ресурсами, в то время как оригинал остается доступным для будущих дизайнов. Используйте справочник по трансформативным функциям при добавлении эффектов, выходящих за рамки этой лаборатории.
Альтернативный текст (alt) и подписи выполняют схожие, но разные задачи. Альтернативный текст передает видимый, релевантный контент, когда изображение недоступно. Подпись к портфолио может добавлять редакционное обрамление для каждого посетителя. Повторное использование одной и той же шаблонной фразы для обеих целей стирает это различие.
Зарегистрируйтесь для получения надстройки Cloudinary AI Vision. Сервер вызывает AI Vision General через Analyze API, используя неизменяемый идентификатор ресурса Cloudinary. Один запрос включает JSON Schema, которая требует раздельных строк для alt-текста и подписи:
Структурированный вывод возвращается в виде строки JSON внутри data.analysis.responses[0].value. Приложение анализирует ее, проверяет обе строки с помощью Zod и отклоняет некорректный или слишком большой вывод перед тем, как что-либо записать. Затем оно сохраняет результат вместе с ресурсом:
Вспомогательная функция считывает неизменяемый идентификатор ресурса и сравнивает сохраненные метаданные с сгенерированными значениями. Это делает источником истины Cloudinary, а не состояние React.
Два автоматических триггера охватывают новые изображения:
- Upload Widget возвращает неизменяемый идентификатор ресурса (asset ID), публичный идентификатор (public ID) и версию. Маршрут описания проверяет эти значения по недавнему помеченному тегом изображению в точной папке портфолио перед вызовом AI Vision.
- Генерация изображений уже работает за маркером редактирования, поэтому ее маршрут вызывает AI Vision сразу после проверки папки.
Старое изображение без метаданных отображает одну историю генерации с действием Cloudinary AI. Его Server Action принимает только assetId и маркер редактирования. Он не принимает написанные вручную альтернативные тексты или подписи.
Ознакомьтесь с полным рабочим процессом в src/lib/cloudinary-description.ts, src/app/api/describe/route.ts и src/app/actions.ts.
Ознакомьтесь с полным рабочим процессом в src/lib/cloudinary-description.ts, src/app/api/describe/route.ts и src/app/actions.ts.
Сгенерированный ИИ текст по-прежнему нуждается в редакционной политике проверки. Автоматизация устраняет повторяющийся текст, но она не гарантирует соответствие Руководству по доступности веб-контента (WCAG) и не предотвращает любые некорректные выводы.
Запустите проект:
Откройте http://localhost:3000, загрузите изображение и посмотрите, как оно появится. Затем выполните важный тест:
- Обновите страницу в браузере.
- Убедитесь, что загруженный ресурс остался на месте.
- Откройте Generate и создайте одно управляемое изображение стандартного уровня.
- Убедитесь, что ответ подтверждает папку и сохраняет метаданные ИИ.
- Откройте панель сведений любого ресурса.
- Изучите доступный только для чтения альтернативный текст от ИИ и подпись к портфолио.
- Снова обновите страницу и убедитесь, что оба поля сохранились без необходимости ручного ввода.
- Откройте Delivery Board и изучите сгенерированный URL.
Приложение также предоставляет эндпоинт /api/health. Он сообщает логические значения готовности и папку ресурсов, не возвращая учетные данные.
Перед развертыванием запустите полную локальную проверку:
Эта команда запускает ESLint, TypeScript и оптимизированную производственную сборку.
URL доставки Cloudinary обычно использует res.cloudinary.com. При первом запросе на трансформацию Cloudinary создает или извлекает производный ресурс и доставляет его через настроенный CDN. Последующие запросы могут обслуживаться из пограничных кэшей (edge caches), расположенных близко к пользователям.
Приватные дистрибуции CDN и пользовательские хосты доставки требуют настройки учетной записи. Cloudinary также предлагает возможности интеллектуальной и динамической доставки через несколько CDN для подходящих учетных записей, как правило, в качестве корпоративных функций доставки. Они настраиваются на уровне учетной записи и доставки Cloudinary, а не активируются переключателем состояния React.
Media Quest показывает активное имя хоста доставки и принимает опциональные значения NEXT_PUBLIC_CLOUDINARY_SECURE_DISTRIBUTION и NEXT_PUBLIC_CLOUDINARY_PRIVATE_CDN. Устанавливайте их только после того, как Cloudinary настроит эту дистрибуцию для вашей учетной записи. Ознакомьтесь с расширенными опциями доставки URL от Cloudinary для получения актуальных требований.
Это разделение обладает огромной силой: код приложения запрашивает медиавариант, а конфигурация доставки определяет, как этот ответ доставляется посетителю.
Демонстрация включает несколько паттернов, которые подходят для использования в продакшене:
- Изображения в галерее имеют встроенные размеры, подсказки для адаптивных размеров и ленивую загрузку (lazy loading) за пределами основного контента.
- Видеоплеер динамически импортируется только тогда, когда это необходимо для просмотра деталей видео.
- Поиск использует useDeferredValue, обеспечивая отзывчивость ввода при фильтрации галереи.
- Альтернативный текст хранится вместе с ресурсом, а не разбросан по коду страниц.
- Состояния загрузки, пустого результата, ошибки, деморежима и незавершенной настройки четко разграничены.
- Статус загрузки использует вежливую область динамического обновления (live region).
- Анимация движения уменьшается, если посетитель запрашивает уменьшение движения.
- Учетные данные сервера хранятся в модуле, защищенном с помощью server-only.
Производительность — это системное свойство. Cloudinary уменьшает вес медиафайлов, в то время как Next.js управляет рендерингом и границами JavaScript. Наилучший результат достигается при осознанном использовании обоих инструментов.
Как только основная проверка заработает, расширяйте функциональность по одному шагу за раз:
- Добавьте подписанные загрузки, когда пользователям требуются динамические, доверенные параметры загрузки.
- Добавьте аутентификацию и папки ресурсов для каждого пользователя.
- Добавьте постоянные ограничения для каждого пользователя на кредиты Image Generation API.
- Добавьте постоянные ограничения для каждого пользователя на токены анализа AI Vision.
- Создайте рабочий процесс модерации на основе вебхуков или тегирования с помощью ИИ.
- Выполняйте пагинацию результатов Search API с помощью next_cursor.
- Добавьте панель сравнения, которая регистрирует переданные байты через Performance API браузера.
- Создавайте карточки для соцсетей с помощью трансформации наложения Cloudinary.
- Добавьте авторизованный процесс удаления с подтверждением и журналом аудита.
- Подключите настроенную частную CDN или корпоративную учетную запись multi-CDN и сравните заголовки кэша в разных регионах.
Самый важный принцип проектирования остается прежним: сохраняйте оригинал один раз, выполняйте привилегированные операции на сервере и создавайте готовый к доставке медиаконтент «на краю» (at the edge).
Готовы начать разработку? Зарегистрируйте бесплатную учетную запись Cloudinary сегодня и создавайте более качественные визуальные медиа-продукты.











