29

According to Facebook graph API we can request a user profile picture with this (example):

https://graph.facebook.com/1489686594/picture

But the real image URL of the previous link is:

http://profile.ak.fbcdn.net/hprofile-ak-snc4/hs356.snc4/41721_1489686594_527_q.jpg

If you type the first link on your browser, it will redirect you to the second link.

Is there any way to get the full URL (second link) with Ruby/Rails, by only knowing the first URL?

(This is a repeat of this question, but for Ruby)

Community
  • 1
  • 1
neon
  • 2,811
  • 6
  • 30
  • 44
  • possible duplicate of [How can I get the final URL after redirects using Ruby?](http://stackoverflow.com/questions/4867652/how-can-i-get-the-final-url-after-redirects-using-ruby) – lulalala Aug 08 '13 at 02:54
  • possible duplicate of [In Ruby, How do I get the destination URL of a shortened URL?](http://stackoverflow.com/questions/5532362/in-ruby-how-do-i-get-the-destination-url-of-a-shortened-url) – Ivan Chau Mar 07 '15 at 09:16
  • I used [final_redirect_url](https://rubygems.org/gems/final_redirect_url) gem to get the final redirected url. – Indyarocks May 03 '17 at 05:18

7 Answers7

52

This was already answered correctly, but there's a much simpler way:

res = Net::HTTP.get_response(URI('https://graph.facebook.com/1489686594/picture'))
res['location']
FelipeC
  • 9,123
  • 4
  • 44
  • 38
15

You can use Net::Http and read the Location: header from the response

require 'net/http'
require 'uri'

url = URI.parse('http://www.example.com/index.html')
res = Net::HTTP.start(url.host, url.port) {|http|
  http.get('/index.html')
}
res['location']
Michael Papile
  • 6,836
  • 30
  • 30
  • 4
    You can use url.request_uri instead of '/index.html'. – FelipeC May 02 '14 at 05:19
  • 3
    what if there are more than one redirect? will it get the final one? or just the first? – Sean May 28 '15 at 19:41
  • As @Sean mentioned, it wont follow all redirects. Also it doesnt work for `bit.ly` links. I wrote a function that follows all redirects and also works for url shorteners, see my solution below. – dcts Dec 07 '19 at 13:21
9

You've got HTTPS URLs there, so you will handle that...

require 'net/http'
require 'net/https' if RUBY_VERSION < '1.9'
require 'uri'

u = URI.parse('https://graph.facebook.com/1489686594/picture')

h = Net::HTTP.new u.host, u.port
h.use_ssl = u.scheme == 'https'

head = h.start do |ua|
  ua.head u.path
end

puts head['location']
smathy
  • 26,283
  • 5
  • 48
  • 68
  • Actually, in your example `u.path` will be `"url.com"` because `"url.com"` is a valid URI, it's just that as a URI it contains no host, port or scheme - it's seen as all path by `URI.parse`. If you want that data to be interpreted as a hostname then you'll need to do extra steps before passing it to `URI.parse` – smathy Jan 31 '13 at 17:29
  • ua.head <= does this mean we're sending a HEAD request only? or are you getting the entire page? – Henley Feb 08 '13 at 14:28
  • It's just a `HEAD` request, because all the OP was looking for was the `Location:` response header. – smathy Feb 08 '13 at 18:35
  • ...and yes Luccas, if your URI has no path then you'd need to add some code to handle that too. This was not designed to be some generic lib that you could import and use in any application. It was a solution to the specific question - hence the hard-coded URI in my `parse` line. – smathy Feb 08 '13 at 18:38
4

I know this is an old question, but I'll add this answer for posterity:

Most of the solutions I've seen only follow a single redirect. In my case, I had to follow multiple redirects to get the actual final destination URL. I used Curl (via the Curb gem) like so:

result = Curl::Easy.perform(url) do |curl|
  curl.head = true
  curl.follow_location = true
end
result.last_effective_url
markquezada
  • 8,444
  • 6
  • 45
  • 52
2

You can check the response status code and get the final URL recursively using something like get_final_redirect_url method:

  require 'net/http'

  def get_final_redirect_url(url, limit = 10)
    uri = URI.parse(url)
    response = ::Net::HTTP.get_response(uri)
    if response.class == Net::HTTPOK
      return uri
    else
      redirect_location = response['location']
      location_uri = URI.parse(redirect_location)
      if location_uri.host.nil?
        redirect_location = uri.scheme + '://' + uri.host + redirect_location
      end
      warn "redirected to #{redirect_location}"
      get_final_redirect_url(redirect_location, limit - 1)
    end
  end

I was facing the same issue. I solved it and built a gem final_redirect_url around it, so that everyone can benefit from it.

You can find the details on uses here.

Indyarocks
  • 643
  • 1
  • 6
  • 26
  • You're not doing anything with the `limit`. Probably need something like `return if limit.zero?` or similar. – Hunter Oct 19 '18 at 18:08
  • 1
    @Hunter The default limit is 10, if there is a recursive redirection, which means, if limit is not explicitly provided, it'll go to a maximum of 10 level of depth until it finds the final url. – Indyarocks Oct 31 '18 at 06:25
  • hmm. I must be missing something. It looks like your recursion could continue infinitely because there's nothing terminating it. I see that you're decrementing `limit` with each iteration, however, where in the method body do you instruct it to stop once the `limit` hits zero? – Hunter Oct 31 '18 at 07:20
  • 1
    You've a valid point. So far, the last response had always been Net::HTTPOK, so I didn't get into infinite loop. But I agree, it should have a return when limit is zero. Thanks for pointing that out. – Indyarocks Nov 01 '18 at 11:27
0

If you want a solution that:

  • does not use gems
  • follows all redirects
  • works also with url-shortening services
require 'net/http'
require 'uri'

def follow_redirections(url)   
  response = Net::HTTP.get_response(URI(url))
  until response['location'].nil?
    response = Net::HTTP.get_response(URI(response['location']))
  end
  response.uri.to_s
end

# EXAMPLE USAGE
follow_redirections("https://graph.facebook.com/1489686594/picture") 
# => https://static.xx.fbcdn.net/rsrc.php/v3/yo/r/UlIqmHJn-SK.gif
dcts
  • 1,479
  • 15
  • 34
0

Yeah, "Location" response header tell you the actual image URL.

However, if you use the picture as the user's profile image on your site, I recommend you to use "https://graph.facebook.com/:user_id/picture" style URL instead of actual image URL. Otherwise, your users will see lots of "not found" images, or outdated profile images in the future.

You just put "https://graph.facebook.com/:user_id/picture" as the "src" attribute of "img" tag. They browser gets the updated image of the user.

ps. I have such troubles on my site with Twitter & Yahoo! OpenID now..

nov matake
  • 938
  • 5
  • 6