Загрузка изображений на S3 в GraphQL с использованием Rails и Paperclip
Ссылка на оригинал - http://graphqlme.com/2017/09/16/upload-images-to-s3-in-graphql-using-rails-and-p…, автор публикации - Matt Engledowl
Когда я впервые начал использовать GraphQL, одна из наиболее неприятных вещей заключалась в загрузке изображений в GraphQL. Это довольно просто реализовать в REST, и если вы не знаете, как это сделать, то можете быстро найти информацию. Существует множество учебных пособий по настройке загрузки изображений в облако S3 на Amazon в rails, но, черт возьми, было сложно найти что-то на эту тему в GraphQL! Это одна из тех вещей, которые, как я понял, казались болезненно очевидными, но переход от REST к GraphQL может занять некоторое время. Имея это в виду, я хотел поделиться тем, как я загружаю изображения на S3 с помощью API GraphQL.
Предположим, у вас есть модель User, в которую вы хотите добавить изображения профиля. У вас также есть UserType диаграмма в GraphQL, а также поле QueryType для поиска пользователя по идентификатору findUser. Все это может выглядеть примерно так:
# graphql/types/user_type.rb Types::UserType = GraphQL::ObjectType.define do name 'UserType' description 'A user for the application' field :id, types.ID field :firstName, types.String, property: :first_name field :lastName, types.String, property: :last_name field :email, types.String end # graphql/types/query_type.rb Types::QueryType = GraphQL::ObjectType.define do name 'Query' field :findUser, Types::UserType, 'Find a user by id' do argument :id, !types.ID, 'The id of the user to find' resolve ->(_obj, args, _ctx) { User.find(args[:id]) } end end # graphql/types/mutation_type.rb Types::MutationType = GraphQL::ObjectType.define do name 'Mutation' field :updateUser, Types::UserType, 'Update a user profile by id' do argument :id, !types.ID, 'The id for the user to update' resolve ->(_obj, args, _ctx) { u = User.find(args[:id]) u.update(args.to_h) u } end end
Итак, у нас есть начальное место - очевидно, наша мутация сейчас не очень полезна, но мы вернемся к этому. Следуем далее и добавим в наш Gemfile paperclip и aws-sdk и установим пакет. Нужно убедиться, что у вас установлен ImageMagick (инструкции можно найти в документации gem, ссылка выше).
Следующий шаг - запуск paperclip. В документации мы добавим наше приложение к модели User и создадим переход для добавления столбцов, которые нам понадобятся:
# user.rb class User < ApplicationRecord has_attached_file :profile_image validates_attachment_content_type :profile_image, content_type: /\Aimage\/.*\z/ end # migration class AddProfileImageToUsers < ActiveRecord::Migration[5.1] def up add_attachment :users, :profile_image end def down remove_attachment :users, :profile_image end end
Не забудьте запустить миграции. Это добавит некоторые функции нашей модели User, что позволит нам загружать изображение, взаимодействовать с ним и проверять его формат. Теперь мы можем обновить нашу схему GraphQL, чтобы вернуть это изображение, добавив поле к нашему UserType:
field :profileImageUrl, types.String do resolve ->(user, _args, _ctx) { user.profile_image.url } end
И вот мы добрались до мутации, где можно загрузить изображение! Кто-то рекомендует делать отдельную конечную точку, которую вы используете для этого, и просто пропускать GraphQL. Но я хочу делать все, что есть в GraphQL, так вот что мы собираемся делать. Теперь, одна из вещей, которая на самом деле не задокументирована (или, по крайней мере, не очень хорошо), заключается в том, что она может принимать изображение с кодировкой base64 для загрузки. Поскольку у нас на самом деле нет возможности обрабатывать загрузки изображений точно так же в GraphQL, как и в REST, мы просто собираемся принять строковый аргумент, содержащий кодированную версию base64 изображения, которое мы пытаемся загрузить. Итак, давайте вернемся к нашей мутации updateUser и добавим новые аргументы, чтобы она выглядела так:
field :updateUser, Types::UserType, 'Update a user profile by id' do argument :id, !types.ID, 'The id for the user to update' argument :profileImageBase64, as: :profile_image do type types.String description 'The base64 encoded version of the profile image to upload.' end argument :profileImageName, types.String, as: :profile_image_file_name, default_value: 'profile-image.jpg' resolve ->(_obj, args, _ctx) { u = User.find(args[:id]) u.update(args.to_h) u } end
У нас есть два новых аргумента, которые мы можем пройти сейчас, и profileImageBase64 - это будет наш образ, закодированный в base64, и укажет на profile_image свойство на нашем User, и profileImageName - это имя для нашего изображения, которое по умолчанию соответствует «profile-image». JPG». Этот важно, поскольку по умолчанию paperclip будет просто называть его «data» без расширения, что затрудняет фактическое использование. В рабочей среде вы, вероятно, захотите сделать некоторую проверку имени изображения или даже установить это автоматически, а не подвергать ее клиенту, но я оставлю любой из них как упражнение, если вы хотите это сделать.
Посмотрим, что у нас есть! Убедитесь, что у вас есть пользователь в базе данных, затем сгенерируйте строку с кодировкой base64. Я использую https://www.base64-image.de/, потому что это так просто - вы просто загружаете файл, щелкаете кнопкой «копировать изображение» - и готово! У вас есть кодированное изображение, скопированное в буфер обмена. Теперь мы можем запустить нашу мутацию:
mutation { updateUser(id: 1, profileImageBase64: "[paste your base64 encoded image here]") { id profileImageUrl } }
Мы должны получить что-то вроде этого:
{ "data": { "updateUser": { "id": "1", "profileImageUrl": "/system/users/profile_images/000/000/001/original/profile-image.jpg?1504992676" } }
Так что загрузка работает! Если нужно, мы можем перейти к нашей папке проекта и развернуться, начиная с /public, до местоположения, указанного в нашем URL выше, и мы должны увидеть изображение. Это все еще не совсем то, что мы хотим - нам нужны эти образы, чтобы их подтолкнули к S3. Прежде чем мы начнем с этого, убедитесь, что у вас есть следующее уже есть:
- Аккаунт AWS
- Бакет S3 для проекта
- Название вашего бакета
- Идентификатор ключа доступа
- Ваш секретный ключ доступа
- Область, в которой находится ваш бакет
ПРИМЕЧАНИЕ. Для целей этого поста в блоге мы будем делать все локально. Если вы делаете это для производственного приложения, убедитесь, что вы используете production.rb для этих настроек, а не development.rb.
Чтобы сделать загрузку paperclip на S3, нам нужно будет установить некоторые детали конфигурации. Откройте config/environments/development.rb и добавьте следующее:
config.paperclip_defaults = { storage: :s3, path: '/:class/:attachment/:id_partition/:style/:filename', s3_protocol: 'https', s3_credentials: { bucket: ENV['S3_BUCKET_NAME'], access_key_id: ENV['AWS_ACCESS_KEY_ID'], secret_access_key: ENV['AWS_SECRET_ACCESS_KEY'], s3_region: ENV['AWS_REGION'] } }
Установите соответствующие переменные среды. Существует несколько способов сделать это, но я предпочитаю dotenv для управления переменными среды, позволяя размещать их в .env файле в корневом каталоге. Если вы идете по этому маршруту, убедитесь, что добавили .env в .gitignore. Если вы этого не сделаете, ваши учетные данные могут быть скомпрометированы.
Перезагрузите сервер и повторите попытку повторной работы. Она должна быть успешной, и вы должны увидеть, что он возвращает URL-адрес вашего изображения, который указывает на него в вашем бакете S3. Теперь вы можете загружать изображения через свой API GraphQL, и все, что ваш клиент должен знать, как это сделать, - это кодирование изображения base64 (для этого есть много библиотек). На этом этапе все готово! Поздравляем, теперь вы можете загружать файлы на S3 через GraphQL в rails! Если вы застряли или хотите просмотреть код, он доступен на моем GitHub здесь.
Хотите больше полезных советов? Смотрите и подписывайтесь на наш канал! Здесь я публикую лучшие советы для пользователей Андроид, Windows, iOS и Mac OS. Также вы можете задать мне любой вопрос, подписавшись на канал.