Skip to content

Instantly share code, notes, and snippets.

@MichaelCurrie
Last active December 11, 2024 18:22
Show Gist options
  • Save MichaelCurrie/19394abc19abd0de4473b595c0e37a3a to your computer and use it in GitHub Desktop.
Save MichaelCurrie/19394abc19abd0de4473b595c0e37a3a to your computer and use it in GitHub Desktop.

Revisions

  1. Michael Currie renamed this gist Jun 9, 2017. 1 changed file with 0 additions and 0 deletions.
  2. Michael Currie revised this gist Jun 9, 2017. 1 changed file with 1 addition and 0 deletions.
    Original file line number Diff line number Diff line change
    @@ -0,0 +1 @@
    #Drag-and-drop upload files, via JavaScript, to a simple Python 3 HTTP server
  3. Michael Currie revised this gist Jun 9, 2017. No changes.
  4. Michael Currie revised this gist Jun 9, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion upload_handler.py
    Original file line number Diff line number Diff line change
    @@ -11,7 +11,7 @@ class FileUploadHTTPRequestHandler(SimpleHTTPRequestHandler):
    files in the same folder as this script.
    """
    protocol_version = "HTTP/1.0"
    protocol_version = "HTTP/1.1"

    def do_POST(self):
    """Handle a POST request."""
  5. Michael Currie created this gist Jun 9, 2017.
    17 changes: 17 additions & 0 deletions dropzone.css
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,17 @@
    body {
    font-family: "Arial", sands-serif;
    }

    .dropzone {
    width: 300px;
    height: 300px;
    border: 2px dashed #ccc;
    color: #ccc;
    line-height: 300px;
    text-align: center;
    }

    .dropzone.dragover {
    border-color: #000;
    color: #000;
    }
    19 changes: 19 additions & 0 deletions dropzone.html
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,19 @@
    <!doctype html>
    <html>
    <head>
    <meta charset="utf-8" />
    <title>Drag n' Drop</title>
    <link rel="stylesheet" type="text/css" href="dropzone.css" />
    <script src="dropzone.js"></script>
    </head>
    <body>
    <h1 id="title">Let's try some drag and drop uploading!</h1>

    <div id="dropzone_element" class="dropzone">
    Drop files here to upload
    </div>

    <div id="upload_results_element">
    </div>
    </body>
    </html>
    57 changes: 57 additions & 0 deletions dropzone.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,57 @@
    // Handle drag and drop into a dropzone_element div:
    // send the files as a POST request to the server
    "use strict";

    // Only start once the DOM tree is ready
    if(document.readyState === "complete") {
    createDropzoneMethods();
    } else {
    document.addEventListener("DOMContentLoaded", createDropzoneMethods);
    }

    function createDropzoneMethods() {
    let dropzone = document.getElementById("dropzone_element");

    dropzone.ondragover = function() {
    this.className = "dropzone dragover";
    return false;
    }

    dropzone.ondragleave = function() {
    this.className = "dropzone";
    return false;
    }

    dropzone.ondrop = function(e) {
    // Stop browser from simply opening that was just dropped
    e.preventDefault();
    // Restore original dropzone appearance
    this.className = "dropzone";

    upload_files(e.dataTransfer.files)
    }
    }

    function upload_files(files) {
    let upload_results = document.getElementById("upload_results_element");
    let formData = new FormData(),
    xhr = new XMLHttpRequest();

    console.log("Dropped " + String(files.length) + " files.");
    for(let i=0; i<files.length; i++) {
    formData.append("file", files[i]);
    }

    xhr.onreadystatechange = function() {
    if(xhr.readyState === XMLHttpRequest.DONE) {
    alert(xhr.responseText);
    }

    console.log(xhr.response);
    upload_results.innerHTML = this.response;
    }

    console.log("Let's upload files: ", formData);
    xhr.open('POST', 'upload_handler.py', true); // async = true
    xhr.send(formData);
    }
    133 changes: 133 additions & 0 deletions upload_handler.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,133 @@
    # -*- coding: utf-8 -*-
    import re
    import sys
    import os
    import json
    from http.server import SimpleHTTPRequestHandler, HTTPServer


    class FileUploadHTTPRequestHandler(SimpleHTTPRequestHandler):
    """An HTTP Server that accepts POST requests and saves them as
    files in the same folder as this script.
    """
    protocol_version = "HTTP/1.0"

    def do_POST(self):
    """Handle a POST request."""
    # Save files received in the POST
    wasSuccess, files_uploaded = self.handle_file_uploads()

    # Compose a response to the client
    response_obj = {
    "wasSuccess": wasSuccess,
    "files_uploaded": files_uploaded,
    "client_address": self.client_address
    }

    response_str = json.dumps(response_obj)

    self.log_message(response_str)

    # Send our response code, header, and data
    self.send_response(200)
    self.send_header("Content-type", "Application/json")
    self.send_header("Content-Length", len(response_str))
    self.end_headers()
    self.wfile.write(response_str.encode('utf-8'))

    def read_line(self):
    line_str = self.rfile.readline().decode('utf-8')
    self.char_remaining -= len(line_str)
    return line_str

    def handle_file_uploads(self):
    """
    Take the post request and save any files received to the same folder
    as this script.
    Returns
    wasSuccess: bool: whether the process was a success
    files_uploaded: list of string: files that were created
    """
    self.char_remaining = int(self.headers['content-length'])
    # Find the boundary from content-type, which might look like:
    # 'multipart/form-data; boundary=----WebKitFormBoundaryUI1LY7c2BiEKGfFk'
    boundary = self.headers['content-type'].split("=")[1]

    basepath = self.translate_path(self.path)
    # Strip this script's name from the path so it's just a folder
    basepath = os.path.dirname(basepath)

    # ----WebKitFormBoundaryUI1LY7c2BiEKGfFk
    line_str = self.read_line()
    if not boundary in line_str:
    self.log_message("Content did NOT begin with boundary as " +
    "it should")
    return False, []

    files_uploaded = []
    while self.char_remaining > 0:
    # Breaking out of this loop on anything except a boundary
    # an end-of-file will be a failure, so let's assume that
    wasSuccess = False

    # Content-Disposition: form-data; name="file"; filename="README.md"
    line_str = self.read_line()
    filename = re.findall('Content-Disposition.*name="file"; ' +
    'filename="(.*)"', line_str)
    if not filename:
    self.log_message("Can't find filename " + filename)
    break
    else:
    filename = filename[0]
    filepath = os.path.join(basepath, filename)
    try:
    outfile = open(filepath, 'wb')
    except IOError:
    self.log_message("Can't create file " + str(filepath) +
    " to write; do you have permission to write?")
    break

    # Content-Type: application/octet-stream
    line_str = self.read_line()

    # Blank line
    line_str = self.read_line()

    # First real line of code
    preline = self.read_line()
    # Loop through the POST until we find another boundary line,
    # signifying the end of this file and the possible start of another
    while self.char_remaining > 0:
    line_str = self.read_line()

    # ----WebKitFormBoundaryUI1LY7c2BiEKGfFk
    if boundary in line_str:
    preline = preline[0:-1]
    if preline.endswith('\r'):
    preline = preline[0:-1]
    outfile.write(preline.encode('utf-8'))
    outfile.close()
    self.log_message("File '%s' upload success!" % filename)
    files_uploaded.append(filename)
    # If this was the last file, the session was a success!
    wasSuccess = True
    break
    else:
    outfile.write(preline.encode('utf-8'))
    preline = line_str

    return wasSuccess, files_uploaded


    if __name__ == "__main__":
    httpd = HTTPServer(("", 8000), FileUploadHTTPRequestHandler)
    sa = httpd.socket.getsockname()
    print("Serving HTTP on", sa[0], "port", sa[1], "...")
    try:
    httpd.serve_forever()
    except KeyboardInterrupt:
    print("\nKeyboard interrupt received, exiting.")
    httpd.server_close()
    sys.exit(0)