How to Post a Photo to Facebook with Python

I don't know why you'd want to add content to facebook.com, but sometimes people ask you to do this for them. Adding photos can be a little tricky.

I was recently trying to do this in a Flask application using Flask OAuth. Unfortunately, if you pass a file along with the data to your remote method call, Facebook throws a dreaded "requires upload file" message. After doing a little research, it seems the problem has to do with the the file data getting signed by the oauth2 library. All the other data should be signed, then the file data needs to added, unsigned, afterwards.

This means patching the libraries to do what you want. I started going down that road and then I remembered I saw a PHP example where the access token was simply passed along as a querystring paramater and the file was uploaded normally with an HTML form.

Turns out, this works wonderfully and you don't need to jump through all the oauth library hoops.

Here's some code using Flask OAuth to get the access token and Requests to post the data.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
oauth = OAuth()

facebook = oauth.remote_app('facebook',
    base_url = 'https://graph.facebook.com/',
    request_token_url = None,
    access_token_url = '/oauth/access_token',
    authorize_url = 'https://www.facebook.com/dialog/oauth',
    consumer_key = "GET YOUR OWN KEY",
    consumer_secret = "GET YOUR OWN SECRET",
    request_token_params = {'scope': 'publish_stream, photo_upload'},
) 

def get_image_path(filename):
    """Implement this to return an absolute path to 
    the image. Maybe do something to avoid security issues."""
    pass

@app.route('/fb/auth/post/<string:filename>')
def post_to_fb_auth(filename):
    callback_url = 'http://localhost' + url_for('fb_authorized', filename = filename)
    return facebook.authorize(callback=callback_url)

@app.route('/fb/authorized/post/<string:filename>/')
@facebook.authorized_handler
def fb_authorized(resp, filename):
    access_token = resp['access_token']
    image = get_image_path(filename)
    files = { 'source': open(image,'rb'), }
    url = 'https://graph.facebook.com/me/photos?access_token=%s' % access_token
    r = requests.post(url, files=files)
    return redirect(url_for("success"))

Just a few things to note: you have to add your callback url to your Facebook app's configuration. This the field called "Website with Facebook Login". In my example, I'm just using localhost, but you can add a port if you're not on 80.

You'll also need to make sure you have the publish_stream and photo_upload permissions set up in your application.

2013-02-24