john john - 2 months ago 44
Python Question

Python SimpleHTTPServer to receive files

I am using SimpleHTTPServer's do_POST method to receive file. The script is working fine if I upload the png file using curl but whenever I use python request library to upload file, File uploads but become corrupt. Here is the SimpleHTTPServer code

#!/usr/bin/env python
# Simple HTTP Server With Upload.

import os
import posixpath
import BaseHTTPServer
import urllib
import cgi
import shutil
import mimetypes
import re
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO

class SimpleHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
# Simple HTTP request handler with POST commands.

def do_POST(self):
"""Serve a POST request."""
r, info = self.deal_post_data()
print r, info, "by: ", self.client_address
f = StringIO()

if r:
f.write("<strong>Success:</strong>")
else:
f.write("<strong>Failed:</strong>")

length = f.tell()
f.seek(0)
self.send_response(200)
self.send_header("Content-type", "text/html")
self.send_header("Content-Length", str(length))
self.end_headers()
if f:
self.copyfile(f, self.wfile)
f.close()

def deal_post_data(self):
print self.headers
boundary = self.headers.plisttext.split("=")[1]
print 'Boundary %s' %boundary
remainbytes = int(self.headers['content-length'])
print "Remain Bytes %s" %remainbytes
line = self.rfile.readline()
remainbytes -= len(line)
if not boundary in line:
return (False, "Content NOT begin with boundary")
line = self.rfile.readline()
remainbytes -= len(line)
fn = re.findall(r'Content-Disposition.*name="file"; filename="(.*)"', line)
if not fn:
return (False, "Can't find out file name...")
path = self.translate_path(self.path)
fn = os.path.join(path, fn[0])
line = self.rfile.readline()
remainbytes -= len(line)
line = self.rfile.readline()
remainbytes -= len(line)
try:
out = open(fn, 'wb')
except IOError:
return (False, "Can't create file to write, do you have permission to write?")

preline = self.rfile.readline()
remainbytes -= len(preline)
while remainbytes > 0:
line = self.rfile.readline()
remainbytes -= len(line)
if boundary in line:
preline = preline[0:-1]
if preline.endswith('\r'):
preline = preline[0:-1]
out.write(preline)
out.close()
return (True, "File '%s' upload success!" % fn)
else:
out.write(preline)
preline = line
return (False, "Unexpect Ends of data.")



def translate_path(self, path):
"""Translate a /-separated PATH to the local filename syntax.

Components that mean special things to the local file system
(e.g. drive or directory names) are ignored. (XXX They should
probably be diagnosed.)

"""
# abandon query parameters
path = path.split('?',1)[0]
path = path.split('#',1)[0]
path = posixpath.normpath(urllib.unquote(path))
words = path.split('/')
words = filter(None, words)
path = os.getcwd()
for word in words:
drive, word = os.path.splitdrive(word)
head, word = os.path.split(word)
if word in (os.curdir, os.pardir): continue
path = os.path.join(path, word)
return path

def copyfile(self, source, outputfile):
"""Copy all data between two file objects.

The SOURCE argument is a file object open for reading
(or anything with a read() method) and the DESTINATION
argument is a file object open for writing (or
anything with a write() method).

The only reason for overriding this would be to change
the block size or perhaps to replace newlines by CRLF
-- note however that this the default server uses this
to copy binary data as well.

"""
shutil.copyfileobj(source, outputfile)



def test(HandlerClass = SimpleHTTPRequestHandler,
ServerClass = BaseHTTPServer.HTTPServer):
BaseHTTPServer.test(HandlerClass, ServerClass)

if __name__ == '__main__':
test()


client side code to upload a file is here

#!/usr/bin/python

import requests

files = {'file': open('test.png', 'rb')}
r = requests.post('http://192.168.5.134:8000', files=files)
print r.request.headers


File was uploaded successfully but become corrupted.

python request header

SimpleHTTPServer response

Using curl [ curl -F 'file=@test.png' 192.168.5.134:8000/ -v ], file uploaded and opened successfully.

Is there any issue in python-request code?

Answer

curl and request have a slightly different header, curl has an additional empty line while requests doesn't.

Replace preline = self.rfile.readline() with the following block

if line.strip():
    preline = line
else:
    preline = self.rfile.readline()