post-ing a file using python requests and json
Question: I've been given the following curl command as part of API documentation, and
I'm trying to implement it using the requests library.

    
    
    curl -v --cookie cookie.txt -X POST -H 'Accept: application/json' -F 'spot[photo]'=@rails.png -F 'spot[description]'=spot_description -F 'spot[location_id]'=9  -F 'spot[categories][]'='See the Sights' -F 'spot[categories][]'='Learn Something' http://some.server.com/api/v1/spots
    

my python code looks something like this:

    
    
    import requests
    import json
    
    _user = 'redacted'
    _password = 'redacted'
    _session = requests.session()
    _server = 'http://some.server.com'
    
    _hdr = {'content-type': 'application/json', 'accept': 'application/json'}
    
    _login_payload = {
        'user': {
            'email': _user,
            'password': _password
        }
    }
    r = _session.post(_server + "/users/sign_in", data=json.dumps(_login_payload), headers=_hdr)
    print json.loads(r.content)
    
    _spot_payload = {
        'spot': {
            'photo': '@rails.gif',
            'description': 'asdfghjkl',
            'location_id': 9,
            'categories': ['See the Sights',]
        }
    }
    r = _session.post(_server + '/api/v1/spots', data=json.dumps(_spot_payload), headers=_hdr)
    print json.loads(r.content)
    

I've heard tell that you can use open('file').read() to post files, but the
json encoder doesn't much like this, and I'm not sure about a way around it.


Answer: 
    C:\>cat file.txt
    Some text.
    

When you issue this command:

    
    
    C:\>curl -X POST -H "Accept:application/json" -F "spot[photo]=@file.txt"
    -F "spot[description]=spot_description" http://localhost:8888
    

what's being sent looks like this:

> POST / HTTP/1.1 User-Agent: curl/7.25.0 (i386-pc-win32) libcurl/7.25.0
> OpenSSL/0.9.8u zlib/1.2.6 libssh2/1.4.0 Host: localhost:8888 Accept:
> application/json Content-Length: 325 Expect: 100-continue Content-Type:
> multipart/form-data; boundary=----------------------------e71aebf115cd
>
> \------------------------------e71aebf115cd Content-Disposition: form-data;
> name="spot[photo]"; filename="file.txt" Content-Type: text/plain
>
> Some text. \------------------------------e71aebf115cd Content-Disposition:
> form-data; name="spot[description]"
>
> spot_description \------------------------------e71aebf115cd--

As you can see curl sends request with `Content-Type` set to `multipart/form-
data;` Requests [support](http://docs.python-
requests.org/en/latest/user/quickstart/#post-a-multipart-encoded-file) sending
files using the same `Content-Type`. You should use `files` argument for this.

    
    
    (2.7) C:\>python
    Python 2.7.3 (default, Apr 10 2012, 23:24:47) [MSC v.1500 64 bit (AMD64)] on win32
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import requests
    >>> requests.__version__
    '0.11.1'
    >>> requests.post('http://localhost:8888', files={'spot[photo]': open('file.txt', 'rb')}, data={'spot[description]': 'spot_description'})
    <Response [200]>
    

And what's being sent looks like this:

    
    
    POST http://localhost:8888/ HTTP/1.1
    Host: localhost:8888
    Content-Length: 342
    Content-Type: multipart/form-data; boundary=192.168.1.101.1.8000.1334865122.004.1
    Accept-Encoding: identity, deflate, compress, gzip
    Accept: */*
    User-Agent: python-requests/0.11.1
    
    --192.168.1.101.1.8000.1334865122.004.1
    Content-Disposition: form-data; name="spot[description]"
    Content-Type: text/plain
    
    spot_description
    --192.168.1.101.1.8000.1334865122.004.1
    Content-Disposition: form-data; name="spot[photo]"; filename="file.txt"
    Content-Type: text/plain
    
    Some text.
    --192.168.1.101.1.8000.1334865122.004.1--
    

