Quick and Dirty File Drop

2021-10-01

It's sometimes handy to have a script that allows a visitor (from the same network) to visit the page and upload a file. This is sort of like the inverse of python -m http.server. The UI is bare-bones and can be vastly improved (drag and drop is an obvious feature to add), but I've found this useful on a few occasions to transfer files on a LAN with no fuss.

This relies on the python-multipart package...it was an expedient choice when I threw this together, and while the Github repo for python-multipart is updated regularly, the PyPI package for python-multipart is not, so I'd probably port this to use multipart instead if I were to revisit it.

#!/usr/bin/env python3

import multipart
from wsgiref.simple_server import make_server

PAGE="""
<html>
  <head>
    <title>Drop</title>
  </head>
  <body>
    <h1>Drop</h1>
    <div>
      <form method="post" enctype="multipart/form-data">
        <input type="file" id="file" name="filename">
        <input value="Upload" type="submit">
      </form>
    </div>
  </body>
</html>
"""

def drop(environ, start_response):
    fields = {}
    files = {}

    def on_field(field):
        fields[field.field_name.decode()] = field.value

    def on_file(file):
        files[file.field_name.decode()] = {'name': file.file_name.decode(), 'file_object': file.file_object}

    content = PAGE

    if environ['REQUEST_METHOD'] == 'POST':
        multipart_headers = {'Content-Type': environ['CONTENT_TYPE']}
        multipart_headers['Content-Length'] = environ['CONTENT_LENGTH']
        multipart.parse_form(multipart_headers, environ['wsgi.input'], on_field, on_file)
        file_data = files["filename"]
        print(file_data)
        file_object = file_data["file_object"]
        file_object.seek(0)
        data = file_object.read()
        print("The file is {} bytes".format(len(data)))
        with open(file_data["name"], "wb") as f:
            f.write(data)

    content = [content.encode('utf-8')]
    status = '200 OK'
    headers = [('Content-type', 'text/html; charset=utf-8')]
    start_response(status, headers)
    return content


def run():
    port = 10000
    with make_server('', port, drop) as httpd:
        print("Serving on port {}...".format(port))
        httpd.serve_forever()


if __name__ == "__main__":
    run()