Uploading forms+files via Python script -- no luck

Karl Ulbrich karl_python at sub.ulbrich.org
Thu Jul 20 19:58:21 EDT 2000


I've written a script that's supposed to  perform an http POST 
in order to upload a mix of fields and files to a webserver, 
based on examples referred to in previous posts on this topic here. 

To test this script, I wrote a simple python cgi script that receives
a form POST then prints out everything that showed up in FieldStorage.
The test-script shows the form values if I make a simple form 
and post fields and/or files from Internet Explorer (or Netscape).

When I use my upload script to attempt to post, however, I 
never see any cgi form contents.  :-(

Using netcat to catch the raw HTTP traffic, I captured both the 
http upload output of IE and my script, but didn't see any 
differences in formatting/MIME stuff that looked detrimental. 

But, something's gotta be wrong, because Python (and presumably
other webservers -- the real point) doesn't think I've sent a form. :(

Many thanks to anyone who spots the problem!!


Thanks, 

Karl Ulbrich

p.s. It would be great if urllib did this...  I'd offer to do it, 
     but, well, it doesn't work!

............... THE RECEIVE SCRIPT (install as a CGI) ...............
#!/usr/bin/python
# This script simply echos out FieldStorage contents for-debugging
# (this script works just fine)

import sys,os,cgi,glob,string
sys.stderr = sys.stdout
data = cgi.FieldStorage()

print "content-type: text/html\n"
print "<h1>Echo Form Contents</h1>"

print str(len(data.keys())) + " Keys found in data.keys(): " 
for key in data.keys(): print key, '\t'

print "<table border=1>"
for item in dir(data):
        print "<tr>", "<td>", item, "</td>",
        print "<td>", getattr( data, item), "</td>", "</tr>"
print "</table>"

.............. THE (broken) UPLOAD SCRIPT .........................

#!/usr/bin/python
#!/usr/local/bin/python
# 
# Summary: First stab at a general web form-and-file POST uploader
# Author: Karl Ulbrich <karl_python at sub.ulbrich.org>
# 
# Liberally borrows and addapts from: 
# FileTransfer.py - CGI file transfer, may require Python 1.5 or higher
# Prior Author/Version: 
# JeffBauer at bigfoot.com http://starship.python.net/crew/jbauer/cgiupload/
# Prior art:  Aaron Watters, Jim Fulton

import os, sys, traceback
import httplib
#import tempfile
from whrandom import randint
from string import join
from time import time

class FileUploadRequest:
    """Client-side CGI form/file POST upload."""
    def __init__(self, uri, host, port):
        self.uri = uri
        self.host = host
        self.port = port
        self.queryString = None
        self.boundary= '%s%s_%s_%s' % \
                        ('-------', int(time()), os.getpid(), randint(1,10000))
        self.parts = []  ## collect each multipart as a single list-entry
        
    def loadfile(self, filename, fieldname, headerDict=None):
        """Add a file to the multipart upload."""
        f = open(filename, 'rb')
        data = f.read()
        f.close()
        hdr = []
        hdr.append( '%s' % self.boundary )
        hdr.append('Content-Disposition: form-data; name="%s"; filename="%s"' % (fieldname,filename))
        hdr.append('Content-Type: application/octet-stream')
#        hdr.append('Content-Length: %d' % len(data))
        # Add user-defined header values here, if provided. 
        if type(headerDict) == type({}):
            for k in headerDict.keys():
                hdr.append("%s: %s", (k, headerDict[k]))
        part='%s\n\n%s' % ( join(hdr,'\n') , data )
        self.parts.extend( [part] ) 
        
    def loadfield(self, fieldname, data, headerDict=None, ):
        """Add a field to the multipart upload."""
        hdr = []
        hdr.append( '%s' % self.boundary )
        hdr.append('Content-Disposition: form-data; name="%s"' % fieldname )
        #hdr.append('Content-Length: %d' % len(data))
        # Add user-defined header values here, if provided.
        if type(headerDict) == type({}):
            for k in headerDict.keys():
                hdr.append("%s: %s", (k, headerDict[k]))
        part='%s\n\n%s' % ( join(hdr,'\n') , data )
        self.parts.extend( [part] ) 

    def request(self):
        query = '%s\n%s' % ( join(self.parts,'\n'), self.boundary )
        
        print "=====BODY DATA BEGIN (DEBUG)====="
        print query
        print "=====BODY DATA END (DEBUG)======"

        contentType = 'multipart/form-data; boundary=%s' % self.boundary
        contentLength = str(len(query))
        h = httplib.HTTP()
        h.connect(self.host, self.port)
        h.putrequest('POST', self.uri)
        h.putheader('Accept', '*/*')
        h.putheader('Proxy-Connection', 'Keep-Alive')
        h.putheader('User-Agent', 'PyFormsPoster/0.1')
        h.putheader('Content-Type', contentType)
        h.putheader('Content-Length', contentLength)
        h.endheaders()
        h.send(query)
        rcode, rmsg, headers = h.getreply()
        response = h.getfile().read()
        print "Return Code: %s\n" % rcode
        #if rcode != 200:
        #    msg = "error: %s, %s\n%s %s" % (rcode, self.uri, rmsg, response)
        #    #raise FileUploadRequestException(msg)
        #    raise "FileUploadRequestException", msg
        return response

def testecho():
    usage = 'usage: cgiupload filename'
    if len(sys.argv) < 2:
        print usage
        sys.exit(1)
    filename = sys.argv[1]

    uri = '/cgi-bin/echoform.py'
    host = '10.10.10.2'
    port = 80
    #port = 8888

    F = FileUploadRequest(uri, host, port)
    F.loadfile(filename,'file1')
    #F.loadfile(filename,'file2')
    F.loadfield( "yourname" , "Arthur, of Camelot" ) 
    F.loadfield( "summary" , "Perhaps if we built a large wooden Badger..." ) 
    F.loadfield( "details" , "Run Away!" ) 
    F.loadfield( "submit" , "submit the page" ) 
    response = F.request()
    print response

if __name__ == '__main__':
    if os.environ.has_key('SERVER_PROTOCOL'):
        FileUploadAcquisition(os.environ)
    else:
        #test()
        testecho()

........... A SIMPLE HTML FORM ................................
<h1>Sends to echoform.py on 80</h1>
<pre><form action="http://hostname:80/cgi-bin/echoform.py" method="POST" enctype="multipart/form-data">
Your Name <input type='text' name='yourname' size='35'>
Summary   <input type='text' name='summary' size='35'>
File #1   <input name="file1" type="file" size="35">
Details   <textarea name=details ROWS="4" COLS="30" wrap=virtual></textarea>
          <input name="submit" type="submit" value="Upload File(s)">
</form></pre>





More information about the Python-list mailing list