Загрузка изображений на 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 здесь.

Не нашли ответ на свой вопрос? Возможно, вы найдете решение проблемы на нашем канале в Youtube! Здесь мы собрали небольшие, но эффективные инструкции. Смотрите и подписывайтесь на наш youtube-канал!

Смотреть на Youtube

1 Что нужно восстановить?

Видео

MP4, AVI и HD видео хранятся на телефоне и / или по ошибке удаляются вместе с фотографиями и другими медиафайлами.

Контакты

Номера телефонов друзей и знакомых из приложения «Контакты Android», журналы вызовов; Восстановление SIM-карты.

Фото

Удалены файлы JPG / PNG из Галереи Android; фото, загруженные на мобильный, файлы повреждены после восстановления.

Смс и сообщения

Чаты WhatsApp и Facebook, текстовые сообщения в соцсетях, информация на сим-карте

2 Где пропали файлы?

На sd-карте

Фотографии и документы хранятся на SD-картах. Часто на них случайно удаляются файлы

На телефоне

Программы для восстановления не распознают внутреннее хранилище телефона как диск, но есть другие решения.

На USB флешке

Эти небольшие устройства хранения данных часто выходят из строя или на них появляются ошибки чтения.

На HDD или SSD

Несмотря на то, что настольные платформы становятся все менее популярными, проблема потери файлов всегда оставалась.