该模块是python中自带的提供HTTP服务的最上层模块,主要提供文件服务,如请求路径是文件夹,则返回该文件夹目录结构。该模块被webpy继承后用于提供静态文件服务。
SimpleHTTPServer模块自带了运行例子,理解起来也比较容易,这里直接贴上注释源码:
##
# @file $python27$\Lib\SimpleHTTPServer.py
# @brief Python中非常简单的HTTP请求处理类,能处理对静态文件响应
# @date 2015-02-04
# @remark 为减少字符量,文件中的注释和对理解无关的内容都被我删除了,想详细了解请见原文件
__version__ = "0.6"
class SimpleHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
server_version = "SimpleHTTP/" + __version__
# 处理HTTP中GET的请求
def do_GET(self):
f = self.send_head()
if f:
self.copyfile(f, self.wfile)
f.close()
# 处理HTTP中HEAD的请求
def do_HEAD(self):
f = self.send_head()
if f:
f.close()
# 发送响应信息中的头部分,如果是请求文件,将返回该文件对象
def send_head(self):
path = self.translate_path(self.path) # 将相对路径的url转化成本地路径
f = None
if os.path.isdir(path): # 本地路径是目录
if not self.path.endswith('/'): # 对"127.0.0.1/images"的url,将通知客户端重定向到"127.0.0.1/images/"
# redirect browser - doing basically what apache does
self.send_response(301)
self.send_header("Location", self.path + "/")
self.end_headers()
return None
for index in "index.html", "index.htm": # 检查目录下是否有index文件存在,如果有,稍后会把index发送给客户端
index = os.path.join(path, index)
if os.path.exists(index):
path = index
break
else:
return self.list_directory(path) # 未找到index,则列出该目录下的文件和文件夹
ctype = self.guess_type(path) # 获取文件的MIME类型
try:
f = open(path, 'rb')
except IOError:
self.send_error(404, "File not found")
return None
self.send_response(200) # 响应状态码: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 # 返回该文件对象
# 列出path目录下的文件和文件夹,发送响应报文头,以内存文件形式返回显示文件列表的html
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>Directory listing for %s</title>\n" % displaypath)
f.write("<body>\n<h2>Directory listing for %s</h2>\n" % displaypath)
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): # 判断是否为文件链接(以为是*.lnk文件,测试也不是,不知道这是什么)
displayname = name + "@"
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
# 将相对路径的url转化成本地路径
def translate_path(self, path):
# 丢弃url请求中的参数
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
# 拷贝类文件对象,webpy中没用这么粗暴的方式,而是一块一块发送
def copyfile(self, source, outputfile):
shutil.copyfileobj(source, outputfile)
# 获得文件的MIME类型
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['']
# mimetypes是Python自带的标准库,可以根据文件后缀直接得到文件的MIME类型
# 参见http://www.pythonclub.org/modules/mimetypes-txt
if not mimetypes.inited:
mimetypes.init() # try to read system mime.types
extensions_map = mimetypes.types_map.copy()
extensions_map.update({
'': 'application/octet-stream', # Default
'.py': 'text/plain',
'.c': 'text/plain',
'.h': 'text/plain',
})