Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails: open() returns StringIO instead of Tempfile

I have two valid URL's to two images. When I run open() on the first URL, it returns an object of type Tempfile (which is what the fog gem expects to upload the image to AWS). When I run open() on the second URL, it returns an object of type StringIO (which causes the fog gem to crash and burn).

Why is open() not returning a Tempfile for the second URL? Further, can open() be forced to always return Tempfile?

From my Rails Console:

2.2.1 :011 > url1
 => "https://fbcdn-profile-a.akamaihd.net/hprofile-ak-xpf1/v/t1.0-1/c0.0.448.448/10298878_10103685138839040_6456490261359194847_n.jpg?oh=e2951e1a1b0a04fc2b9c0a0b0b191ebc&oe=56195EE3&__gda__=1443959086_417127efe9c89652ec44058c360ee6de" 
2.2.1 :012 > url2
 => "https://fbcdn-profile-a.akamaihd.net/hprofile-ak-xfa1/v/t1.0-1/c0.17.200.200/1920047_10153890268465074_1858953512_n.jpg?oh=5f4cdf53d3e59b8ce4702618b3ac6ce3&oe=5610ADC5&__gda__=1444367255_396d6fdc0bdc158e4c2e3127e86878f9" 
2.2.1 :013 > t1 = open(url1)
 => #<Tempfile:/var/folders/58/lpjz5b0n3yj44vn9bmbrv5180000gn/T/open-uri20150720-24696-1y0kvtd> 
2.2.1 :014 > t2 = open(url2)
 => #<StringIO:0x007fba9c20ae78 @base_uri=#<URI::HTTPS https://fbcdn-profile-a.akamaihd.net/hprofile-ak-xfa1/v/t1.0-1/c0.17.200.200/1920047_10153890268465074_1858953512_n.jpg?oh=5f4cdf53d3e59b8ce4702618b3ac6ce3&oe=5610ADC5&__gda__=1444367255_396d6fdc0bdc158e4c2e3127e86878f9>, @meta={"last-modified"=>"Tue, 25 Feb 2014 19:47:06 GMT", "content-type"=>"image/jpeg", "timing-allow-origin"=>"*", "access-control-allow-origin"=>"*", "content-length"=>"7564", "cache-control"=>"no-transform, max-age=1209600", "expires"=>"Mon, 03 Aug 2015 22:01:40 GMT", "date"=>"Mon, 20 Jul 2015 22:01:40 GMT", "connection"=>"keep-alive"}, @metas={"last-modified"=>["Tue, 25 Feb 2014 19:47:06 GMT"], "content-type"=>["image/jpeg"], "timing-allow-origin"=>["*"], "access-control-allow-origin"=>["*"], "content-length"=>["7564"], "cache-control"=>["no-transform, max-age=1209600"], "expires"=>["Mon, 03 Aug 2015 22:01:40 GMT"], "date"=>["Mon, 20 Jul 2015 22:01:40 GMT"], "connection"=>["keep-alive"]}, @status=["200", "OK"]>

This is how I'm using fog:

tempfile = open(params["avatar"])
user.avatar.store!(tempfile)
like image 312
etayluz Avatar asked Jul 20 '15 22:07

etayluz


1 Answers

I assume you are using Ruby's built-in open-uri library that allows you to download URLs using open().

In this case Ruby is only obligated to return an IO object. There is no guarantee that it will be a file. My guess is that Ruby makes a decision based on memory consumption: if the download is large, it puts it into a file to save memory; otherwise it keeps it in memory with a StringIO.

As a workaround, you could write a method that writes the stream to a tempfile if it is not already downloaded to a file:

def download_to_file(uri)
  stream = open(uri, "rb")
  return stream if stream.respond_to?(:path) # Already file-like

  Tempfile.new.tap do |file|
    file.binmode
    IO.copy_stream(stream, file)
    stream.close
    file.rewind
  end
end

If you're looking for a full-featured gem that does something similar, take a look at "down": https://github.com/janko-m/down

like image 136
Matt Brictson Avatar answered Nov 13 '22 05:11

Matt Brictson