views:

1127

answers:

3

Hi,

I'm interested in writing a short python script which uploads a short binary file (.wav/.raw audio) via a POST request to a remote server.

I've done this with pycurl, which makes it very simple and results in a concise script; unfortunately it also requires that the end user have pycurl installed, which I can't rely on.

I've also seen some examples in other posts which rely only on basic libraries, urllib, urllib2, etc., however these generally seem to be quite verbose, which is also something I'd like to avoid.

I'm wondering if there are any concise examples which do not require the use of external libraries, and which will be quick and easy for 3rd parties to understand - even if they aren't particularly familiar with python.

What I'm using at present looks like,


def upload_wav( wavfile, url=None, **kwargs ):
    """Upload a wav file to the server, return the response."""

    class responseCallback:
        """Store the server response."""
        def __init__(self):
            self.contents=''
        def body_callback(self, buf):
            self.contents = self.contents + buf

        def decode( self ):
            self.contents = urllib.unquote(self.contents)
            try:
                self.contents = simplejson.loads(self.contents)
            except:
                return self.contents

    t = responseCallback()
    c = pycurl.Curl()
    c.setopt(c.POST,1)
    c.setopt(c.WRITEFUNCTION, t.body_callback)
    c.setopt(c.URL,url)
    postdict = [
        ('userfile',(c.FORM_FILE,wavfile)),  #wav file to post                                                                                 
        ]
    #If there are extra keyword args add them to the postdict                                                                                  
    for key in kwargs:
        postdict.append( (key,kwargs[key]) )
    c.setopt(c.HTTPPOST,postdict)
    c.setopt(c.VERBOSE,verbose)
    c.perform()
    c.close()
    t.decode()
    return t.contents

this isn't exact, but it gives you the general idea. It works great, it's simple for 3rd parties to understand, but it requires pycurl.

+1  A: 

How's urllib substantially more verbose? You build postdict basically the same way, except you start with

postdict = [ ('userfile', open(wavfile, 'rb').read()) ]

Once you vave postdict,

resp = urllib.urlopen(url, urllib.urlencode(postdict))

and then you get and save resp.read() and maybe unquote and try JSON-loading if needed. Seems like it would be actually shorter! So what am I missing...?

Alex Martelli
This is actually what I initially tried - I also thought it should/would provide the neatest solution. Unfortunately since it's a file POST it seems I can't get away with just doing this. I've got to handle the multipart/form-data. I ended up going with pycurl initially since encapsulates this encoding procedure nicely, but I was still unhappy as it adds a dependency.
blackkettle
+1  A: 

POSTing a file requires multipart/form-data encoding and, as far as I know, there's no easy way (i.e. one-liner or something) to do this with the stdlib. But as you mentioned, there are plenty of recipes out there.

Although they seem verbose, your use case suggests that you can probably just encapsulate it once into a function or class and not worry too much, right? Take a look at the recipe on ActiveState and read the comments for suggestions:

or see the MultiPartForm class in this PyMOTW, which seems pretty reusable:

I believe both handle binary files.

ars
Thanks, I ended up adapting one of the PyMOTW examples to my purpose. I guess there is no way to get around writing this up. I wonder why something like this has not yet made its way into urllib or urllib2?
blackkettle
A: 

urllib.urlencode doesn't like some kinds of binary data.

Leo