Установите content_type файлов хранилища тумана на s3

Я работаю с Fog и Amazon s3 для управления файлами видео и изображений. У меня было много проблем с настройкой content_type для моих файлов.

При работе с консоли я могу просмотреть и индивидуально обновить content_type каждого файла, а затем запустить save. Однако, когда я пытаюсь запустить обновление для всех файлов в определенном каталоге, я не получаю сообщения об ошибке, но ничего не обновляется. Я запустил несколько разных методов, все с одной и той же основной идеей, и все настроены на печать «сохранено!» если файл сохранится. Методы работают правильно и выводят «сохранено!», но когда я возвращаюсь и проверяю файлы, content_type по-прежнему равен нулю.

Вот пример того, что я делаю:

directory.files.each do |f|
  case f.key.split(".").last
  when "jpg"
    f.content_type = "image/jpeg"
    puts "saved!" if f.save
  when "mov"
    f.content_type = "video/quicktime"
    puts "saved!" if f.save
  end
end

Кроме того, когда я просматриваю и индивидуально обновляю каждый файл, сохранение работает, и content_type обновляется, но данные не сохраняются.

Например:

file = directory.files.first
file.content_type = 'video/quicktime'
file.save         # returns true
file.content_type # returns 'video/quicktime'

Однако, когда я проверяю файл в AWS, тип содержимого по-прежнему нулевой.

Есть ли лучший (постоянный) способ обновления content_type в файлах Fog s3? Я чувствую, что я должен идти об этом неправильно.

Обновление: пытался использовать метод file#copy:

directory.files.each do |f|
  content_type = case f.key.split(".").last
  when "jpg"
    "image/jpeg"
  when "mov"
    "video/quicktime"
  end
  puts "copied!" if f.copy(f.directory.key, f.key, { 'Content-Type' => content_type })
end

Я получил ошибку:

Excon::Errors::BadRequest: Expected(200) <=> Actual(400 Bad Request)

from /Users/marybethlee/.rvm/gems/ruby-2.0.0-p0@mothership/gems/excon-0.22.1/lib/excon/middlewares/expects.rb:6:in `response_call'
from /Users/marybethlee/.rvm/gems/ruby-2.0.0-p0@mothership/gems/excon-0.22.1/lib/excon/connection.rb:355:in `response'
from /Users/marybethlee/.rvm/gems/ruby-2.0.0-p0@mothership/gems/excon-0.22.1/lib/excon/connection.rb:249:in `request'
from /Users/marybethlee/.rvm/gems/ruby-2.0.0-p0@mothership/gems/fog-1.11.1/lib/fog/core/connection.rb:21:in `request'
from /Users/marybethlee/.rvm/gems/ruby-2.0.0-p0@mothership/gems/fog-1.11.1/lib/fog/aws/storage.rb:506:in `request'
from /Users/marybethlee/.rvm/gems/ruby-2.0.0-p0@mothership/gems/fog-1.11.1/lib/fog/aws/requests/storage/copy_object.rb:33:in `copy_object'
from /Users/marybethlee/.rvm/gems/ruby-2.0.0-p0@mothership/gems/fog-1.11.1/lib/fog/aws/models/storage/file.rb:93:in `copy'
from (irb):14
from /Users/marybethlee/.rvm/rubies/ruby-2.0.0-p0/bin/irb:16:in `<main>'

person oolong    schedule 10.07.2013    source источник


Ответы (2)


Если вы просто обновляете метаданные (а не само тело/контент), вы, вероятно, захотите использовать копирование вместо сохранения. Возможно, это неочевидно, но это сохраняет работу на стороне S3, так что она будет НАМНОГО быстрее.

Подпись для копии выглядит так:

copy(target_directory_key, target_file_key, options = {})

Поэтому я думаю, что мое предлагаемое решение должно выглядеть (более или менее) так:

directory.files.each do |f|
  content_type = case f.key.split(".").last
  when "jpg"
    "image/jpeg"
  when "mov"
    "video/quicktime"
  end
  options = {
    'Content-Type' => content_type,
    'x-amz-metadata-directive' => 'REPLACE'
  }
  puts "copied!" if f.copy(f.directory, f.key, options)
end

По сути, это должно сказать S3: «скопируйте мой файл поверх самого себя, но измените этот заголовок». Таким образом, вам не нужно скачивать/перезагружать файл. Это, вероятно, подход, который вы хотите.

Итак, решение в стороне, все еще кажется, что вы нашли ошибку. Не могли бы вы привести пример того, что вы подразумеваете под «индивидуальным обновлением каждого файла»? Просто хочу убедиться, что я точно знаю, что вы имеете в виду, и что я могу видеть рабочие и нерабочие случаи рядом. Кроме того, как/почему вы думаете, что он не обновляет тип содержимого (на самом деле он может обновлять его, но просто неправильно отображает обновленное значение или что-то в этом роде). Бонусные баллы, если вы можете создать проблему здесь, чтобы убедиться, что я не забуду решить ее: https://github.com/fog/fog/issues?state=open

person geemus    schedule 15.07.2013
comment
Итак, я попробовал то, что вы предложили, но получил ошибку: Excon::Errors::BadRequest: Expected(200) <=> Actual(400 Bad Request) я отредактирую свой исходный пост, чтобы включить ошибку, а также примеры того, что я имел в виду под отдельным обновлением. - person oolong; 15.07.2013
comment
Спасибо за обновления. Я обнаружил по крайней мере одну проблему (я упустил из виду, что вам нужно указать «ЗАМЕНИТЬ», поскольку по умолчанию будут использоваться старые метаданные и игнорировать все, что передается в запросе на копирование). Кроме того, немного больше подробностей об ошибке было бы удобно. Не могли бы вы обновить версию тумана и попробовать еще раз? Я не знаю, исправит ли это это, но, по крайней мере, должно дать немного более подробное сообщение об ошибке. Спасибо! - person geemus; 15.07.2013
comment
Ага! да, указание замены исправило ошибку, но это все та же проблема, с которой я сталкивался раньше. Прохождение цикла распечатывает скопированные, но тип содержимого по-прежнему равен нулю для всех файлов, и их индивидуальное обновление похоже на то, что оно работает, но не соответствует aws. - person oolong; 16.07.2013
comment
Чтобы уточнить тогда. Вы делаете запрос, и кажется, что он успешен, как вы ищете тип контента, который возвращает ноль? Вы делаете files.all.each {|file| file.content_type} или files.get(key).content_type или что-то еще? Просто хочу убедиться, что я знаю, что происходит на этом конце (поскольку он может быть установлен на S3, но просто не обновляется/отображается правильно на стороне клиента). В частности, я не думаю, что эта информация включается по умолчанию при выполнении files.all. - person geemus; 16.07.2013
comment
Ты прав. Если я сделаю directory.files.first.content_type, я получу ноль. Но если я передам ключ того же файла в directory.files.get(key).content_type, он вернет "video/quicktime. Это проблема, связанная с S3, или это вообще проблема? Я могу работать с ним теперь, когда я знаю, что происходит. - person oolong; 17.07.2013
comment
Да, это ограничение/проблема, связанная с S3. Вызовы API, которые вы используете для получения одного файла (в отличие от получения списка файлов), возвращают немного разные результаты, как вы, вероятно, хорошо знаете. К сожалению, версия списка файлов не включает много метаданных (вы можете более подробно увидеть, что она включает здесь: docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketGET.html). Так что эта информация просто недоступна там. - person geemus; 17.07.2013
comment
Упс, введите = отправить, а не новую строку! Все это говорит о том, что вы также можете принудительно загрузить информацию, выполнив directory.files.first.reload.content_type, что сделает это за вас. Обратите внимание, что это означает много вызовов API. В конечном счете, я думаю, что либо метод, который вы использовали, либо метод копирования, который мы выяснили, ДОЛЖЕН устанавливать тип содержимого, как ожидалось (к сожалению, это просто не показывает это в списке файлов). Надеюсь, это все прояснит, но обязательно дайте мне знать, если я могу помочь с чем-то еще. Спасибо! - person geemus; 17.07.2013
comment
Круто, спасибо за такую ​​информацию! Метод копирования работал для установки content_type, мне просто пришлось немного походить по кругу, чтобы найти его снова. Приятно знать, что это проблема, чтобы я не попал в нее снова. Спасибо за помощь! - person oolong; 18.07.2013
comment
Не беспокойтесь, я определенно понимаю, насколько это может быть запутанным/удивительным. К сожалению, мы не можем сделать лучше (без выполнения head для каждого объекта или чего-то еще, что не является отличным общим решением, поскольку у вас могут быть тысячи объектов). В любом случае, рад, что вы получили то, что вам нужно! - person geemus; 18.07.2013
comment
Обновление 7 лет спустя: с версией Fog, которую я использую, мне пришлось использовать f.directory.key вместо f.directory, так что это было f.copy(f.directory.key, f.key, options) - person mltsy; 09.05.2020

Я нашел этот обходной путь, чтобы получить тип контента:

directory.files.head(file.key).content_type

где file.key — имя вашего файла.

person Yuxuan Chen    schedule 11.05.2017