[英]Python fast static file serving
What's the fastest way to serve static files in Python? 在Python中提供静态文件的最快方法是什么? I'm looking for something equal or close enough to Nginx's static file serving.
我正在寻找与Nginx的静态文件服务相同或足够接近的东西。
I know of SimpleHTTPServer but not sure if it can handle serving multiple files efficiently and reliably. 我知道SimpleHTTPServer但不确定它是否能够高效可靠地处理多个文件。
Also, I don't mind it being a part of a lib/framework of some sort as long as its lib/framework is lightweight. 另外,我不介意它只是lib /框架的一部分,只要它的lib / framework是轻量级的。
What about FAPWS3 ? 那么FAPWS3呢? One of the selling points:
其中一个卖点:
Static file server
静态文件服务器
FAPWS can be used to serve a huge amount of static file requests.
FAPWS可用于提供大量静态文件请求。 With the help of a async database in the backend, you can use FAPWS as your own Amazon S3.
借助后端中的异步数据库,您可以将FAPWS用作您自己的Amazon S3。
I would highly recommend using a 3rd party HTTP server to serve static files. 我强烈建议使用第三方HTTP服务器来提供静态文件。
Servers like nginx are heavily optimized for the task at hand, parallelized and written in fast languages. 像nginx这样的服务器针对手头的任务进行了大量优化,并以快速语言进行并行化和编写。
Python is tied to one processor and interpreted. Python与一个处理器绑定并进行解释。
Original SimpleHTTPServer from python standard library does NOT " handle serving multiple files efficiently and reliably ". 来自python标准库的原始SimpleHTTPServer不“ 有效且可靠地处理多个文件 ”。 For instance, if you are downloading one file from it, another HTTP access to it must be hovering since
SimpleHTTPServer.py
is a simple singal-thread HTTP server which could only support one connecting simultaneously . 例如,如果您从中下载一个文件,则对它的另一个HTTP访问必须悬停,因为
SimpleHTTPServer.py
是一个简单的单线程HTTP服务器 ,它只能同时支持一个连接 。
Fortunately, note that SimpleHTTPServer.py
use BaseHTTPServer.HTTPServer as handler, which can be wrapped by SocketServer.ForkingMixIn and SocketServer.ThreadingMixIn also from python standard library to support multi-process and multi-thread mode, which could highly enhance simple HTTP server's " efficience and reliability ". 幸运的是,请注意
SimpleHTTPServer.py
使用BaseHTTPServer.HTTPServer作为处理程序,它可以由SocketServer.ForkingMixIn和SocketServer.ThreadingMixIn包装,也可以从python标准库中支持多进程和多线程模式,这可以高度增强简单的HTTP服务器的“ 效率和可靠性 “。
According to this idea, a SimpleHTTPServer with multi-thread/multi-process support modified from original one is given as follows: 根据这个想法,具有从原始支持修改的多线程/多进程支持的SimpleHTTPServer如下:
$ python2.7 ModifiedSimpleHTTPServer.py
usage: ModifiedSimpleHTTPServer.py [-h] [--pydoc] [--port PORT]
[--type {process,thread}] [--root ROOT]
[--run]
Modified SimpleHTTPServer with MultiThread/MultiProcess and IP bind support.
Original: https://docs.python.org/2.7/library/simplehttpserver.html
Modified by: vbem@163.com
optional arguments:
-h, --help show this help message and exit
--pydoc show this module's pydoc
run arguments:
--port PORT specify server port (default: 8000)
--type {process,thread}
specify server type (default: 'thread')
--root ROOT specify root directory (default: cwd '/home/vbem')
--run run http server foreground
NOTE: stdin for input, stdout for result, stderr for logging
For example, ModifiedSimpleHTTPServer.py --run --root /var/log --type process
will run a multi-process HTTP static files server with '/var/log' as its root directory. 例如,
ModifiedSimpleHTTPServer.py --run --root /var/log --type process
将运行一个多进程HTTP静态文件服务器,其中“/ var / log”作为其根目录。
Modified codes are: 修改后的代码是:
#! /usr/bin/env python2.7
# -*- coding: utf-8 -*-
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
r"""Modified SimpleHTTPServer with MultiThread/MultiProcess and IP bind support.
Original: https://docs.python.org/2.7/library/simplehttpserver.html
Modified by: vbem@163.com
"""
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
import os, sys, pwd, posixpath, BaseHTTPServer, urllib, cgi, shutil, mimetypes, socket, SocketServer, BaseHTTPServer
from cStringIO import StringIO
USERNAME = pwd.getpwuid(os.getuid()).pw_name
HOSTNAME = socket.gethostname()
PORT_DFT = 8000
class SimpleHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
server_version = "SimpleHTTP/0.6"
def do_GET(self):
f = self.send_head()
if f:
self.copyfile(f, self.wfile)
f.close()
def do_HEAD(self):
f = self.send_head()
if f:
f.close()
def send_head(self):
path = self.translate_path(self.path)
f = None
if os.path.isdir(path):
if not self.path.endswith('/'):
self.send_response(301)
self.send_header("Location", self.path + "/")
self.end_headers()
return None
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:
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:
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>%s %s</title>\n<body>" % (HOSTNAME, displaypath))
f.write("%s@%s:<strong>%s</strong>\n" % (USERNAME, HOSTNAME, path.rstrip('/')+'/'))
f.write("<hr>\n<ul>\n")
for name in list:
fullname = os.path.join(path, name)
displayname = linkname = name
if os.path.isdir(fullname):
displayname = name + "/"
linkname = name + "/"
if os.path.islink(fullname):
displayname = name + "@"
f.write('<li><a href="%s">%s</a>\n'
% (urllib.quote(linkname), cgi.escape(displayname)))
f.write("</ul>\n<hr>\n<pre>%s</pre>\n</body>\n</html>\n" % __doc__)
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
def translate_path(self, path):
path = path.split('?',1)[0]
path = path.split('#',1)[0]
path = posixpath.normpath(urllib.unquote(path))
words = path.split('/')
words = filter(None, words)
path = os.getcwd()
for word in words:
drive, word = os.path.splitdrive(word)
head, word = os.path.split(word)
if word in (os.curdir, os.pardir): continue
path = os.path.join(path, word)
return path
def copyfile(self, source, outputfile):
shutil.copyfileobj(source, outputfile)
def guess_type(self, path):
base, ext = posixpath.splitext(path)
if ext in self.extensions_map:
return self.extensions_map[ext]
ext = ext.lower()
if ext in self.extensions_map:
return self.extensions_map[ext]
else:
return self.extensions_map['']
if not mimetypes.inited:
mimetypes.init()
extensions_map = mimetypes.types_map.copy()
extensions_map.update({'': 'text/plain'})
class ProcessedHTTPServer(SocketServer.ForkingMixIn, BaseHTTPServer.HTTPServer):
r"""Handle requests in multi process."""
class ThreadedHTTPServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):
r"""Handle requests in a separate thread."""
SERVER_DICT = {
'thread' : ThreadedHTTPServer,
'process' : ProcessedHTTPServer,
}
SERVER_DFT = 'thread'
def run(sCwd=None, sServer=SERVER_DFT, nPort=PORT_DFT, *lArgs, **dArgs):
r"""
"""
sys.stderr.write('start with %r\n' % sys._getframe().f_locals)
if sCwd is not None:
os.chdir(sCwd)
cServer = SERVER_DICT[sServer]
oHttpd = cServer(("", nPort), SimpleHTTPRequestHandler)
sys.stderr.write('http://%s:%s/\n' % (HOSTNAME, nPort))
oHttpd.serve_forever()
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# main
def _main():
r"""Main.
"""
import argparse
oParser = argparse.ArgumentParser(
description = __doc__,
formatter_class = argparse.RawTextHelpFormatter,
epilog = 'NOTE: stdin for input, stdout for result, stderr for logging',
)
oParser.add_argument('--pydoc', action='store_true',
help = "show this module's pydoc",
)
oGroupR = oParser.add_argument_group(title='run arguments', description='')
oGroupR.add_argument('--port', action='store', type=int, default=PORT_DFT,
help = 'specify server port (default: %(default)r)',
)
oGroupR.add_argument('--type', action='store', default=SERVER_DFT, choices=SERVER_DICT.keys(),
help = 'specify server type (default: %(default)r)',
)
oGroupR.add_argument('--root', action='store', default=os.getcwd(),
help = 'specify root directory (default: cwd %(default)r)',
)
oGroupR.add_argument('--run', action='store_true',
help = '\n'.join((
'run http server foreground',
)))
oArgs = oParser.parse_args()
if oArgs.pydoc:
help(os.path.splitext(os.path.basename(__file__))[0])
elif oArgs.run:
return run(sCwd=oArgs.root, sServer=oArgs.type, nPort=oArgs.port)
else:
oParser.print_help()
return 1
return 0
if __name__ == "__main__":
exit(_main())
Meanwhile, the single python file with only 200 lines may satisfy your " in Python " and " lightweight " demands. 同时,只有200行的单个python文件可以满足您的“ Python ”和“ 轻量级 ”需求。
Last but not least, this ModifiedSimpleHTTPServer.py
may be a "killer app" by hand for temporary use, however, Nginx is advised for long term use. 最后但并非最不重要的是,这个
ModifiedSimpleHTTPServer.py
可能是一个临时使用的“杀手级应用程序”,但建议Nginx长期使用。
If you look for a oneliner you can do the following: 如果您寻找oneliner,您可以执行以下操作:
$> python -m SimpleHTTPServer
$> python -m SimpleHTTPServer
This will not fullfil all the task required but worth mentioning that this is the simplest way :-) 这不会满足所有需要的任务,但值得一提的是这是最简单的方法:-)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.