Loading entire directories in Python SimpleHTTPServer

I really like how I can easily share files on the network using SimpleHTTPServer, but I would like to have the option “download the entire directory”. Is there a simple (single liner) way to implement this?

thanks

+4
source share
3 answers

Look at the sources, for example. online here . Right now, if you are calling a server with a directory URL, its index.html file has been submitted or is missing, the list_directory method is list_directory . Presumably, you want to instead create a zip file with the contents of the directory (recursively, I suppose), and serve this? Obviously, there is no way to do this with a one-line change, since you want to replace lines 68-80 (in the send_head method) plus the entire list_directory method, lines 98-137 - this is at least a change of more than 50 lines; -).

If you are fine with changing a few dozen lines, and not just one, and the semantics that I described, you want, of course, you can create the required zipfile as cStringIO.StringIO with the ZipFile class and fill it with os.walk in the directory in question (assuming what you want, recursively, also get all the subdirectories). But it will definitely not be single-line; -).

+5
source

There is not a single liner that would do this, also what do you mean by “download entire directory” like tar or zip?

In any case, you can follow these steps

  • Derive a class from SimpleHTTPRequestHandler or simply copy its code
  • Modify the list_directory method to return a link to "download the whole folder"
  • Modify the copyfile method so that for your links you zip up the entire directory and return it
  • You can cache zip so that you do not zip the folder every time, and whether you see any file being changed or not.

It would be fun :)

+4
source

I made this modification for you, I don't know if you have any better ways to do this, but:

Just save the file (example: ThreadedHTTPServer.py) and access it as:

$ python -m /path/to/ThreadedHTTPServer PORT

BPaste Raw Version

The modification also works in streaming mode, so you will not have problems with loading and navigation at the same time, the code is not organized, but:

 from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler from SocketServer import ThreadingMixIn import threading import SimpleHTTPServer import sys, os, zipfile PORT = int(sys.argv[1]) def send_head(self): """Common code for GET and HEAD commands. This sends the response code and MIME headers. Return value is either a file object (which has to be copied to the outputfile by the caller unless the command was HEAD, and must be closed by the caller under all circumstances), or None, in which case the caller has nothing further to do. """ path = self.translate_path(self.path) f = None if self.path.endswith('?download'): tmp_file = "tmp.zip" self.path = self.path.replace("?download","") zip = zipfile.ZipFile(tmp_file, 'w') for root, dirs, files in os.walk(path): for file in files: if os.path.join(root, file) != os.path.join(root, tmp_file): zip.write(os.path.join(root, file)) zip.close() path = self.translate_path(tmp_file) elif os.path.isdir(path): if not self.path.endswith('/'): # redirect browser - doing basically what apache does self.send_response(301) self.send_header("Location", self.path + "/") self.end_headers() return None else: for index in "index.html", "index.htm": index = os.path.join(path, index) if os.path.exists(index): path = index break else: return self.list_directory(path) ctype = self.guess_type(path) try: # Always read in binary mode. Opening files in text mode may cause # newline translations, making the actual size of the content # transmitted *less* than the content-length! f = open(path, 'rb') except IOError: self.send_error(404, "File not found") return None self.send_response(200) self.send_header("Content-type", ctype) fs = os.fstat(f.fileno()) self.send_header("Content-Length", str(fs[6])) self.send_header("Last-Modified", self.date_time_string(fs.st_mtime)) self.end_headers() return f def list_directory(self, path): try: from cStringIO import StringIO except ImportError: from StringIO import StringIO import cgi, urllib """Helper to produce a directory listing (absent index.html). Return value is either a file object, or None (indicating an error). In either case, the headers are sent, making the interface the same as for send_head(). """ try: list = os.listdir(path) except os.error: self.send_error(404, "No permission to list directory") return None list.sort(key=lambda a: a.lower()) f = StringIO() displaypath = cgi.escape(urllib.unquote(self.path)) f.write('<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">') f.write("<html>\n<title>Directory listing for %s</title>\n" % displaypath) f.write("<body>\n<h2>Directory listing for %s</h2>\n" % displaypath) f.write("<a href='%s'>%s</a>\n" % (self.path+"?download",'Download Directory Tree as Zip')) f.write("<hr>\n<ul>\n") for name in list: fullname = os.path.join(path, name) displayname = linkname = name # Append / for directories or @ for symbolic links if os.path.isdir(fullname): displayname = name + "/" linkname = name + "/" if os.path.islink(fullname): displayname = name + "@" # Note: a link to a directory displays with @ and links with / f.write('<li><a href="%s">%s</a>\n' % (urllib.quote(linkname), cgi.escape(displayname))) f.write("</ul>\n<hr>\n</body>\n</html>\n") length = f.tell() f.seek(0) self.send_response(200) encoding = sys.getfilesystemencoding() self.send_header("Content-type", "text/html; charset=%s" % encoding) self.send_header("Content-Length", str(length)) self.end_headers() return f Handler = SimpleHTTPServer.SimpleHTTPRequestHandler Handler.send_head = send_head Handler.list_directory = list_directory class ThreadedHTTPServer(ThreadingMixIn, HTTPServer): """Handle requests in a separate thread.""" if __name__ == '__main__': server = ThreadedHTTPServer(('0.0.0.0', PORT), Handler) print 'Starting server, use <Ctrl-C> to stop' server.serve_forever() 
+3
source

Source: https://habr.com/ru/post/1305952/


All Articles