I want to share a small snippet of code to upload a file to a remote
server
as a “multipart/form-data” . The function below gets two arguments.
The
server url ( ex: http://server.org/upload ) and a filename. First the
filename
encoded as a “form-data”, then we use httplib to POST it to the
server. Since
httplib wants the host + path in separate stages, we have to parse the
url using urlparse.
The receiving server must accept the data and return the location of
the
newly created resource. There are many snippet on the web, but I felt
they
were all incomplete or too messy. The encode function below is
actually part
of a snippet I found googling around. Happy uploading.
import httplib
import urlparse
def upload(url,filename):
def encode (file_path, fields=[]):
BOUNDARY = '----------bundary------'
CRLF = '\r\n'
body = []
# Add the metadata about the upload first
for key, value in fields:
body.extend(
['--' + BOUNDARY,
'Content-Disposition: form-data; name="%s"' % key,
'',
value,
])
# Now add the file itself
file_name = os.path.basename(file_path)
f = open(file_path, 'rb')
file_content = f.read()
f.close()
body.extend(
['--' + BOUNDARY,
'Content-Disposition: form-data; name="file"; filename="%s"'
% file_name,
# The upload server determines the mime-type, no need to set it.
'Content-Type: application/octet-stream',
'',
file_content,
])
# Finalize the form body
body.extend(['--' + BOUNDARY + '--', ''])
return 'multipart/form-data; boundary=%s' % BOUNDARY, CRLF.join(body)
if os.path.exists(filename):
content_type, body = encode(filename)
headers = { 'Content-Type': content_type }
u = urlparse.urlparse(url)
server = httplib.HTTPConnection(u.netloc)
server.request('POST', u.path, body, headers)
resp = server.getresponse()
server.close()
if resp.status == 201:
location = resp.getheader('Location', None)
else :
print resp.status, resp.reason
location = None
return location
Since I’m working with Django, this is the server part. Few remarks:
I create the file name using uuid1(). This is an easy way to create unique identifier. A bit over killing maybe.
I assume a model myfiles
and a form UploadFileForm
that you can easily guess.
the function handle_uploaded_file
is the procedure that actually saves the file on the disk. This is standard.
I return a “Location” where the user can access the file. You have to create a small view to serve the file.
import uuid
from django.http import HttpResponse
import os
import datetime
from myapp.models import myfiles
from myapp.forms import UploadFileForm
def handle_uploaded_file(f,n):
destination = open(n, 'wb+')
for chunk in f.chunks():
destination.write(chunk)
destination.close()
def upload(request):
if request.method == 'POST':
form = UploadFileForm(request.POST, request.FILES)
if form.is_valid():
ip = request.META['REMOTE_ADDR']
u = str(uuid.uuid1())
uploaded = datetime.datetime.now()
fname = os.path.join(baseupdir, u)
handle_uploaded_file(request.FILES['file'],fname)
size = os.path.getsize(fname)
d = myfiles(fname=fname,size=size,uploaded=uploaded,ip=ip,uuid=u).save()
response = HttpResponse(content="", status=201)
response["Location"] = "/file?uuid=%s" % u
return response # 10.2.2 201 Created
else :
return HttpResponse(status=400) # 10.4.1 400 Bad Request
else :
return HttpResponse(status=400) # 10.4.1 400 Bad Request