Receiving an HTTP file as a web request, without using a framework, is not as trivial as this:
In order to guarantee the passage of the data of the file, along with other possible form data, and other things that go in an HTTP post, the thing is well chatinha.
I made a program in Python 3- without using a framework, just using the http.server module to receive a file - it works, but it "rips" the file data from inside the HTTP post without parsing correctly . Certainly there are dozens of corner-cases that are not treated (not that they are not treated right - they simply are not treated). In particular the post data is read as "bytes" and has no cool Python search methods available and cut strings. In particular I do a necessary trick: a transparent conversion of these bytes to string with the codec "latin1" - this preserves the numerical value of each byte, as a unicode character of equivalent value in the string - and in the string object, I can use split , find, slices, etc ...
On the other hand, I think there's a nice example of how to use the "http.server" of Python 3 (BaseHTTPServer module) in Python2.
# coding: utf-8
from http.server import HTTPServer, BaseHTTPRequestHandler
import cgi
UPLOAD_DIR = "/tmp/"
class FileSaver(BaseHTTPRequestHandler):
def _headers(self):
self.send_response(200)
self.send_header("Content-type", "text/html;charset=utf-8")
self.end_headers()
def do_POST(self):
files = []
try:
raw_file_content = self.rfile.read(int( self.headers.get('content-length')))
data = raw_file_content.decode("latin1")
while True:
try:
header, data = data.split("\r\n\r\n", 1)
except ValueError:
break
boundary_str = header.split("\r\n", 1)[0]
if "filename" in header:
file_data = data.split("\r\n" + boundary_str)[0].encode("latin1")
filename = header.split('filename="')[1].split('"')[0]
with open(UPLOAD_DIR + filename, "wb") as file_:
file_.write(file_data)
files.append(filename)
data = data[data.find(boundary_str):]
if len(data) < 2:
break
except Exception as exc:
self.send_response(500)
self.send_header("Content-type", "text/plain;charset=utf-8")
self.end_headers()
self.wfile.write(str(exc).encode("utf-8"))
raise
self._headers()
self.wfile.write("""<h1 style="color:red">upload of {} ok</h1>""".format(filename).encode("utf-8"))
return
def do_GET(self):
self._headers()
self.wfile.write("""<h1 style="color:red">Alô mundo!!</h1>
<form method="post" action="/" enctype="multipart/form-data">
arquivo: <input type="file" name="arquivo"><br/>
<input type="submit" value="ok" / >
</form>
""".encode("utf-8"))
return
if __name__ == "__main__":
server = HTTPServer(("0.0.0.0", 8000), FileSaver)
server.serve_forever()
Of course using a framework like Flask, it is well easier to do this.
A view that saves a file that has been in a Flask forum, using the WTF forms extension (with the framework already taking care of all the above) would be something like:
from flask import Flask, render_template
import flask_wtf
app = Flask(__name__)
class FileForm(form):
file = flask_wtf.file.FileField
@route("/upload", methods=["POST"])
def upload(form, id):
form.file.data.save("/tmp/" + form.file.data.filename)
return render_template(...)
(This code in Flask is not complete and has not been tested - the one in Python raw works the way it is)
Now the code to post a file via HTTP - again, if using "raw" Python can be very complicated - but in this case, we can use the "requests" method that accepts a "file" parameter directly (it receives a dictionary with the name of the file field and an open file, which we wish to send):
>>> requests.post("http://localhost:8000", files={"arquivo": open("imagem.png", "rb") } )
Only this call uploads the file to the first server in Listing 1 above.
Now, it's one thing to want to play with it, another is to want to use it in production - in that case it's better to use the framework, of course, or use xmlrpc (or jsonrpc) - it's going to be an order of magnitude easier. p>