Receiving an HTTP file as a web request without using a framework is not so trivial:
To ensure the passage of the file data, along with other possible form data, and other things that go in an HTTP post, the thing is very boring.
I made a program in Python 3- without using a framework, just using the http.server module to receive a file - it works, but starts "in the box" the file data from inside the HTTP post, without parsing correctly. Surely there are dozens of corner-cases that are not treated (it is not that they are not treated properly - they are simply not treated). In particular the post data is read as "bytes" and does not have available the cool Python methods of searching and cutting strings. In particular I do a necessary gambiarra: a transparent conversion of these "bytes" to string with the "latin1" codec - this preserves the numeric 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 it’s a nice example of how to use Python 3’s "http.server" (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’s well easier to do that.
A view that saves a file that came to a forum in Flask using the WTF form extension (with the framework already taking care of all the cornercases 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 above raw Pythno works as is)
Already the code to post a file by HTTP - again, if using Python "raw" can be quite complicated - but in this case, we can use the method "requests" that accepts a parameter "file" 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 list 1 server above.
Now, it’s one thing to want to play with it, it’s another to want to use it in production - in this case it’s better to either use the framework, of course, or use xmlrpc (or jsonrpc) - it’s going to be an easier order of magnitude.
This post: Sockets in java or that: Creation and communication with daemon, maybe help.
– Gomiero
Maybe you can install a "ready" server on one side (e.g., Apache), and use the: urllib2 to request. It seems to be easier.
– Gomiero
Apache is not a "ready server" for receiving text files - you need a server-side application that is invoked by Apache to handle the http request that sends a file.
– jsbueno
python also has requests on its own project site have some examples http://pt.python-requests.org/en/latest/
– user37612
Guys, I have already solved this problem using a python mini server and more details. You find it interesting that I post the solution here as an answer?
– Christian Felipe