Rails: вариант Active Storage генерирует дополнительный файл - PullRequest
0 голосов
/ 20 марта 2019

Итак, после прочтения официальной документации, я использую вариант Active Storage таким образом

<%= image_tag user.avatar.variant(resize: "100x100") %>

Проблема в том, что когда я смотрю на свое хранилище S3, я замечаю, что у меня есть оригинальный файл (длянапример, размер 22 КБ) и файл варианта, который содержит файл 2 КБ (размер изменен).

Я знаю, что в документации сказано:

.. "Когда браузер нажимаетПри альтернативном URL-адресе Active Storage будет лениво преобразовывать исходный большой двоичный объект в указанный формат и перенаправлять его в новое расположение службы. "

Но есть ли способ запретить Active Storage создавать дополнительный файл варианта?Как сделать вариант один, оригинал.Таким образом, вместо 24 КБ ресурсов в S3 у меня есть только 2 КБ.

Большое спасибо!

РЕДАКТИРОВАТЬ:

Мне удается решить проблему, применяя некоторые ответы, если кто-нибудь придетпо этому поводу:

def resize_avatar2

  avatar_f = avatar.last
  if avatar_f.nil?
    return
  end

  resized_image = MiniMagick::Image.read(avatar_f.download)
  resized_image = resize_with_crop(resized_image, 300, 300)
  v_filename = avatar_f.filename
  v_content_type = avatar_f.content_type
  avatar.all.each { |imagen| imagen.purge }

  avatar.attach(
    io: File.open(resized_image.path),
    filename: v_filename,
    content_type: v_content_type)
end

Создайте помощника, включите его с помощью следующих методов:

    def resize_with_nocrop(image, w, h)

    w_original = image[:width].to_f
    h_original = image[:height].to_f

    if (w_original*h != h_original * w)
      if w_original*h >= h_original * w
        # long width
        w_result = w
        h_result = w_result * (h_original / w_original)
      elsif w_original*h <= h_original * w
        # long height
        h_result = h
        w_result = h_result * (w_original / h_original)
      end
    else
       # good proportions
       h_result = h
       w_result = w
    end

    #
    image.resize("#{w_result}x#{h_result}")
    return image
  end

  def resize_with_crop(img, w, h, options = {})
  gravity = options[:gravity] || :center

  w_original, h_original = [img[:width].to_f, img[:height].to_f]

  op_resize = ''

  # check proportions
  if w_original * h < h_original * w
    op_resize = "#{w.to_i}x"
    w_result = w
    h_result = (h_original * w / w_original)
  else
    op_resize = "x#{h.to_i}"
    w_result = (w_original * h / h_original)
    h_result = h
  end

  w_offset, h_offset = crop_offsets_by_gravity(gravity, [w_result, h_result], [ w, h])

  img.combine_options do |i|
    i.resize(op_resize)
    i.gravity(gravity)
    i.crop "#{w.to_i}x#{h.to_i}+#{w_offset}+#{h_offset}!"
  end

  img
end

GRAVITY_TYPES = [ :north_west, :north, :north_east, :east, :south_east, :south, :south_west, :west, :center ]
def crop_offsets_by_gravity(gravity, original_dimensions, cropped_dimensions)
  raise(ArgumentError, "Gravity must be one of #{GRAVITY_TYPES.inspect}") unless GRAVITY_TYPES.include?(gravity.to_sym)
  raise(ArgumentError, "Original dimensions must be supplied as a [ width, height ] array") unless original_dimensions.kind_of?(Enumerable) && original_dimensions.size == 2
  raise(ArgumentError, "Cropped dimensions must be supplied as a [ width, height ] array") unless cropped_dimensions.kind_of?(Enumerable) && cropped_dimensions.size == 2

  original_width, original_height = original_dimensions
  cropped_width, cropped_height = cropped_dimensions

  vertical_offset = case gravity
    when :north_west, :north, :north_east then 0
    when :center, :east, :west then [ ((original_height - cropped_height) / 2.0).to_i, 0 ].min
    when :south_west, :south, :south_east then (original_height - cropped_height).to_i
  end

  horizontal_offset = case gravity
    when :north_west, :west, :south_west then 0
    when :center, :north, :south then [ ((original_width - cropped_width) / 2.0).to_i, 0 ].min
    when :north_east, :east, :south_east then (original_width - cropped_width).to_i
  end

  return [ horizontal_offset, vertical_offset ]
end

1 Ответ

1 голос
/ 20 марта 2019

Если вы хотите заменить оригиналы вариантами, я думаю, что лучший способ - изменить размер изображения перед его загрузкой.

например. Используя params[:user][:avatar], чтобы найти путь к временному файлу, запустите на нем ffmpeg (для него есть гемы), а затем загрузите результат.

В качестве альтернативы вы можете выполнить обработку изображения в работе после его загрузки:

  • В вашей модели с изображением (User) добавьте after_create :resize_image
  • Затем метод resize_image с ImagesJob.resize(self).perform_later
  • Тогда в вашем классе ImagesJob (метод resize(user)) вы бы:
    • Сгенерируйте вариант: url = user.avatar.variant(resize: "100x100").url (сгенерированный URL Rails, а не S3)
    • Получить изображение: image = open(url).read (require 'open-uri' необходимо)
    • Найдите URL-адрес варианта S3, удалите его, найдите исходный URL-адрес изображения, удалите его (для этого необходимо пройти ActiveStorage::Blob (active_storage_blobs таблицу).
    • Загрузить image как новое изображение user.avatar.attach(StringIO.new(image)); user.save

(не проверено, но, вероятно, довольно близко)

...