Skip to content

Instantly share code, notes, and snippets.

@omz
Last active August 8, 2023 14:44
Show Gist options
  • Save omz/3823483 to your computer and use it in GitHub Desktop.
Save omz/3823483 to your computer and use it in GitHub Desktop.
File Transfer script for Pythonista (iOS)
# File Transfer for Pythonista
# ============================
# This script allows you to transfer Python files from
# and to Pythonista via local Wifi.
# It starts a basic HTTP server that you can access
# as a web page from your browser.
# When you upload a file that already exists, it is
# renamed automatically.
# From Pythonista's settings, you can add this script
# to the actions menu of the editor for quick access.
#
# Get Pythonista for iOS here:
# http://omz-software.com/pythonista
from BaseHTTPServer import BaseHTTPRequestHandler
import urlparse
import urllib
import cgi
import editor
import console
from socket import gethostname
import os
from cStringIO import StringIO
TEMPLATE = ('<!DOCTYPE html><html><head>' +
'<link href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.1.1/'+
'css/bootstrap-combined.min.css" rel="stylesheet"></head><body>' +
'<div class="navbar"><div class="navbar-inner">' +
'<a class="brand" href="#">Pythonista File Transfer</a>' +
'</div></div><div class="container">' +
'<h2>Upload File</h2>{{ALERT}}'
'<p><form action="/" method="POST" enctype="multipart/form-data">' +
'<div class="form-actions">' +
'<input type="file" name="file"></input><br/><br/>' +
'<button type="submit" class="btn btn-primary">Upload</button>' +
'</div></form></p><hr/><h2>Download Files</h2>' +
'{{FILES}}</div></body></html>')
class TransferRequestHandler(BaseHTTPRequestHandler):
def get_unused_filename(self, filename):
if not os.path.exists(filename):
return filename
basename, ext = os.path.splitext(filename)
suffix_n = 1
while True:
alt_name = basename + '-' + str(suffix_n) + ext
if not os.path.exists(alt_name):
return alt_name
suffix_n += 1
def get_html_file_list(self):
buffer = StringIO()
buffer.write('<ul>')
root_dir = os.path.expanduser('~/Documents')
files = []
for dn, dc, filenames in os.walk(root_dir):
for fn in filenames:
rel_dir = os.path.relpath(dn, root_dir)
if rel_dir != '.':
rel_file = os.path.join(rel_dir, fn)
else:
rel_file = fn
files.append(rel_file)
for filename in files:
if os.path.splitext(filename)[1] == '.py':
buffer.write('<li><a href="%s">%s</a></li>' % (filename, filename))
buffer.write('</ul>')
return buffer.getvalue()
def do_GET(self):
parsed_path = urlparse.urlparse(self.path)
path = parsed_path.path
if path == '/':
html = TEMPLATE
html = html.replace('{{ALERT}}', '')
html = html.replace('{{FILES}}', self.get_html_file_list())
self.send_response(200)
self.send_header('Content-Type', 'text/html')
self.end_headers()
self.wfile.write(html)
return
file_path = urllib.unquote(path)[1:]
if os.path.isfile(file_path):
self.send_response(200)
self.send_header('Content-Type', 'application/x-python')
self.send_header('Content-Disposition',
'attachment; filename=%s' % file_path)
self.end_headers()
with open(file_path, 'r') as f:
data = f.read()
self.wfile.write(data)
else:
html = TEMPLATE
html = html.replace('{{ALERT}}',
'<div class="alert alert-error">File not found</div>')
html = html.replace('{{FILES}}', self.get_html_file_list())
self.send_response(404)
self.send_header('Content-Type', 'text/html')
self.end_headers()
self.wfile.write(html)
def do_POST(self):
form = cgi.FieldStorage(fp=self.rfile, headers=self.headers,
environ={'REQUEST_METHOD':'POST',
'CONTENT_TYPE':self.headers['Content-Type']})
self.send_response(200)
self.send_header('Content-Type', 'text/html')
self.end_headers()
field_item = form['file']
uploaded_filename = None
dest_filename = None
file_data = field_item.file.read()
file_len = len(file_data)
uploaded_filename = field_item.filename
dest_filename = self.get_unused_filename(uploaded_filename)
with open(dest_filename, 'w') as f:
f.write(file_data)
editor.reload_files()
del file_data
html = TEMPLATE
if uploaded_filename != dest_filename:
message = '%s uploaded (renamed to %s).' % (uploaded_filename,
dest_filename)
else:
message = '%s uploaded.' % (uploaded_filename)
html = html.replace('{{ALERT}}',
'<div class="alert alert-success">%s</div>' % (message))
html = html.replace('{{FILES}}', self.get_html_file_list())
self.wfile.write(html)
if __name__ == '__main__':
console.clear()
from BaseHTTPServer import HTTPServer
server = HTTPServer(('', 8080), TransferRequestHandler)
URL = 'http://%s.local:8080' % gethostname()
print 'Open this page in your browser:'
console.set_font('Helvetica-Bold', 30)
print URL
console.set_font()
print 'Tap the stop button when you\'re done.'
server.serve_forever()
@fsalamero
Copy link

Great!

@pablozen
Copy link

pablozen commented Apr 8, 2013

Very Good!

@tacores
Copy link

tacores commented Nov 4, 2015

Very useful!

@pedrohdz
Copy link

Hey all,
I took the original FileTransfer.py, and added a few features:

  • SSL
  • Basic auth
  • RESTish endpoint with JSON responses
  • Upload to specific directories.
  • Other little things.

It can be found here:

Hope it's of some use to someone out there!

Cheers!

@itorres-1
Copy link

Hello, new user here (Github, python, pythonista). I wrote some code on python on my computer and would like to use the .py file on pythonista but can't seem to import it. It appears this code here is exactly for that purpose but can someone please explain exactly how I'm supposed to use it? Thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment