Необходимо изменить каталог хранения файлов в корзине S3 (Carrierwave/Fog)

Я использую Carrierwave с 3 отдельными моделями для загрузки фотографий на S3. Я сохранил настройки по умолчанию для загрузчика, который должен был хранить фотографии в корневой корзине S3. Затем я решил хранить их в подкаталогах в соответствии с названием модели, например /аватары, предметы/ и т. д., в зависимости от модели, из которой они были загружены...

Затем я заметил, что файлы с таким же именем перезаписываются, и когда я удалял запись модели, фотография не удалялась.

С тех пор я изменил store_dir из настройки, специфичной для загрузчика, следующим образом:

  def store_dir
    "items"
  end

на общий, в котором хранится фотография под идентификатором модели (я использую mongo FYI):

  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end

Вот в чем проблема. Я пытаюсь переместить все фотографии уже в S3 в соответствующий «каталог» в S3. Насколько я знаю, в S3 нет каталогов как таковых. У меня проблемы с задачей rake. Поскольку я изменил store_dir, Carrierwave ищет все фотографии, ранее загруженные в неправильный каталог.

namespace :pics do
  desc "Fix directory location of pictures on s3"
  task :item_update => :environment do
    connection = Fog::Storage.new({
      :provider                 => 'AWS',
      :aws_access_key_id => 'XXXX',
      :aws_secret_access_key => 'XXX'
    })
    directory = connection.directories.get("myapp-uploads-dev")

    Recipe.all.each do |l|
      if l.images.count > 0
        l.items.each do |i|
          if i.picture.path.to_s != ""
            new_full_path = i.picture.path.to_s
            filename = new_full_path.split('/')[-1].split('?')[0]
            thumb_filename = "thumb_#{filename}"
            original_file_path = "items/#{filename}"
            puts "attempting to retrieve: #{original_file_path}"
            original_thumb_file_path = "items/#{thumb_filename}"
            photo = directory.files.get(original_file_path) rescue nil
            if photo
              puts "we found: #{original_file_path}"
              photo.expires = 2.years.from_now.httpdate
              photo.key = new_full_path
              photo.save
              thumb_photo = directory.files.get(original_thumb_file_path) rescue nil
              if thumb_photo
                puts "we found: #{original_thumb_file_path}"
                thumb_photo.expires = 2.years.from_now.httpdate
                thumb_photo.key = "/uploads/item/picture/#{i.id}/#{thumb_filename}"
                thumb_photo.save
              end
            end
          end
        end
      end
    end
  end
end

Итак, я просматриваю все рецепты, ищу элементы с фотографиями, определяю старый путь Carrierwave, пытаюсь обновить его новым на основе изменения store_dir. Я думал, что если бы я просто обновил photo.key с новым путем, это сработало бы, но это не так.

Что я делаю неправильно? Есть ли лучший способ выполнить вопрос здесь?

Вот что я сделал, чтобы это заработало...

namespace :pics do
  desc "Fix directory location of pictures"
  task :item_update => :environment do
    connection = Fog::Storage.new({
      :provider                 => 'AWS',
      :aws_access_key_id => 'XXX',
      :aws_secret_access_key => 'XXX'
    })
    bucket = "myapp-uploads-dev"
    puts "Using bucket: #{bucket}"
    Recipe.all.each do |l|
      if l.images.count > 0
        l.items.each do |i|
          if i.picture.path.to_s != ""
            new_full_path = i.picture.path.to_s
            filename = new_full_path.split('/')[-1].split('?')[0]
            thumb_filename = "thumb_#{filename}"
            original_file_path = "items/#{filename}"
            original_thumb_file_path = "items/#{thumb_filename}"
            puts "attempting to retrieve: #{original_file_path}"
            # copy original item
            begin
              connection.copy_object(bucket, original_file_path, bucket, new_full_path, 'x-amz-acl' => 'public-read')
              puts "we just copied: #{original_file_path}"
            rescue
              puts "couldn't find: #{original_file_path}"
            end
            # copy thumb
            begin
              connection.copy_object(bucket, original_thumb_file_path, bucket, "uploads/item/picture/#{i.id}/#{thumb_filename}", 'x-amz-acl' => 'public-read')
              puts "we just copied: #{original_thumb_file_path}"
            rescue
              puts "couldn't find thumb: #{original_thumb_file_path}"
            end

          end
        end
      end
    end
  end
end

Возможно, не самая красивая вещь в мире, но это сработало.


person aressidi    schedule 26.09.2013    source источник
comment
Я бы тоже ожидал, что это сработает. Есть ли ошибки или файлы просто не существуют там, где вы ожидаете? Если есть небольшое число, это может работать нормально, но особенно с большим числом вы можете использовать copy_object, как упоминает Джереми ниже (поскольку он делает все намного быстрее, и вам не нужно ничего скачивать).   -  person geemus    schedule 27.09.2013
comment
Спасибо за это, это было полезно для меня, проходящего через то же самое. Одна вещь, которую вы, возможно, захотите знать в будущем, это то, что вы можете получить имя файла напрямую, без необходимости анализировать путь: i.picture.file.filename сделает это в вашем случае.   -  person Nick Gottlieb    schedule 20.12.2013


Ответы (1)


Вам нужно напрямую взаимодействовать с объектами S3, чтобы перемещать их. Вы, вероятно, захотите взглянуть на copy_object и delete_object в жемчужине Fog, которую CarrierWave использует для взаимодействия с S3.

https://github.com/fog/fog/blob/8ca8a059b2f5dd2abc232dd2d2104fe6d8c41919/lib/fog/aws/requests/storage/copy_object.rb

https://github.com/fog/fog/blob/8ca8a059b2f5dd2abc232dd2d2104fe6d8c41919/lib/fog/aws/requests/storage/delete_object.rb

person Jeremy Green    schedule 27.09.2013
comment
Спасибо за помощь, Джереми, это поставило меня на правильный путь. Публикация моего решения в качестве редактирования для всех, кто приходит сюда. - person aressidi; 27.09.2013