Ruby open возвращает строку вместо файла?

При попытке open() удаленные изображения, некоторые возвращаются как StringIO и другие возвращаются как File... как заставить File?

data = open("http://graph.facebook.com/61700024/picture?type=square")
=> #<StringIO:0x007fd09b013948>

data = open("http://28.media.tumblr.com/avatar_7ef57cb42cb0_64.png")
=> #<StringIO:0x007fd098bf9490>

data = open("http://25.media.tumblr.com/avatar_279ec8ee3427_64.png")
=> #<File:/var/folders/_z/bb18gdw52ns0x5r8z9f2ncj40000gn/T/open-uri20120229-9190-mn52fu>

Я использую Paperclip для сохранения удаленных изображений (которые хранятся в S3), поэтому в основном хочу сделать:

user = User.new
user.avatar = open(url)
user.save

2 ответа

Решение

Open-URI имеет 10KB ограничение на StringIO объекты, что-нибудь выше этого, и он сохраняет его как временный файл.

Один из способов справиться с этим - изменить константу, Open-URI принимает за предел StringIO объекты. Вы можете сделать это, установив константу в 0;

OpenURI::Buffer.send :remove_const, 'StringMax' if OpenURI::Buffer.const_defined?('StringMax')
OpenURI::Buffer.const_set 'StringMax', 0

Добавьте это к своему инициализатору, и вы должны быть готовы.

В то время как решение Steigers - это простое универсальное решение, некоторые из нас могут быть оттолкнуты его "неприятным хакерским" чувством и тем, как оно меняет поведение в глобальном масштабе. Включая другие драгоценные камни и такие, которые могут принести пользу или зависеть от этой функции OpenURI. Ofc. Вы также можете использовать описанный выше подход, а затем, когда вы закончите, сбросить константу обратно к ее первоначальному значению, и из-за GIL вы можете избежать неприятностей такого рода (хотя тогда убедитесь, что держитесь подальше от jruby и потоков!),

В качестве альтернативы вы можете сделать что-то вроде этого, что в основном гарантирует, что если вы получите поток, он будет передан во временный файл:

def write_stream_to_a_temp_file(stream)
  ext = begin
    "."+MIME::Types[stream.meta["content-type"]].first.extensions.first
  rescue #In case meta data is not available
    #It seems sometimes the content-type is binary/octet-stream
    #In this case we should grab the original ext name.
    File.extname(stream.base_uri.path)
  end
  file = Tempfile.new ["temp", ext]
  begin
    file.binmode
    file.write stream.read
  ensure
    file.flush rescue nil
    file.close rescue nil
  end
  file
end

# and when you want to enforce that data must be a temp file then just...
data = write_stream_to_a_temp_file data unless data.is_a? Tempfile
Другие вопросы по тегам