-
-
Save MichaelCurrie/19394abc19abd0de4473b595c0e37a3a to your computer and use it in GitHub Desktop.
#Drag-and-drop upload files, via JavaScript, to a simple Python 3 HTTP server |
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; | |
} |
<!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> |
// 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); | |
} |
# -*- 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.1" | |
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) |
it works fine.But if i try to upload images or .png files it doesnt work.The size of the image file at destination is 0KB.could you have any solution for that?
@venkatDesh I did a quick check in the code above and I can't see anything immediately obvious why it would specifically be causing problems for you for .png or other image formats but not other file extensions.
Sorry, I don't have time to investigate your error further, but if you find a solution please feel free to post it here to benefit others. Thanks for understanding.
@MichaelCurrie i got the solution for that problem.By the wayThankyou for your reply.
I just changed the encode and decode method in upload_handle.py from 'utf-8' to 'ISO-8859-1' and it works fine.
And one more thing what if i want to custmozie the upload location while dragging do you have any idea for that?.
If you mean customize the place on the server the files are saved, you just need to change these lines:
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)
To a hardcoded basepath
:
basepath = os.path.join('C', 'venkatDesh', 'havefun')
The JavaScript drag-and-drop example is from https://www.youtube.com/watch?v=hqSlVvKvvjQ, but in the video he used PHP code on the server side. I decided to use Python 3 instead.
I was already serving my folders statically using the following simple Python 3 server:
python -m http.server 8000
But this server doesn't handle POST requests, which is what the drag-and-drop Javascript client-side code generates.
So I looked up the source code for how that simple command actually works:
https://github.com/python/cpython/blob/3.4/Lib/http/server.py
I added the
do_POST
function from https://gist.github.com/UniIsland/3346170.I modified @UniIsland's function to my coding tastes and also made multiple-file uploads possible.
To test, simply run the Python script and then point your browser to
localhost:8000/dropzone.html
Then you should be able to drag-and-drop one or more files into the upload
div
element.