Python 实现 Filebrowser
异步IO实现,HTTP1.0, 200,404,405等信息。实现GET,HEAD, POST方法
一、 代码实现
FileBrowser.py
p# -*- coding: utf-8 -*-
# @File : FileBrowser.py
# @Author : zy
import argparse
import asyncio
import urllib.parse
import mimetypes
import os, json, time
html404 = b'<html><head><title>404 Not Found</title></head><body><center><h1>404 Not Found</h1></center></body></html>'
html405 = b'<html><head><title>405 Method Not Found</title></head><body><center><h1>405 Method Not Found</h1></center></body></html>'
async def dispatch(reader, writer):
try:
data = await reader.readline()
if data == b'\r\n':
return
message = data.decode().split(' ')
print(message) # ['GET', '/', 'HTTP/1.1\r\n']
# message[0] method
# message[1] dir
if message[1] == "./favicon.ico":
return
if message[0] == "GET": # Method GET returns 200 response.
dir_path = urllib.parse.unquote(message[1])
if dir_path[0] != '/':
dir_path = '/' + dir_path[0]
relative_path = root_dir + dir_path
print(relative_path)
if os.path.isfile(relative_path): # open the file and guess the mimetype.
mimetype = mimetypes.guess_type(relative_path)[0]
if mimetype is None:
mimetype = 'application/octet-stream'
httpresponse = [b'HTTP/1.0 200 OK\r\n',
bytes('Content-Type:', encoding='UTF-8') +
bytes(mimetype + '; charset=utf-8\r\n', encoding='UTF-8'),
bytes('Content-Length: ' + str(os.path.getsize(relative_path)) + '\r\n',
encoding="UTF-8"),
b'\r\n']
file = open(relative_path, 'rb')
httpresponse.append(file.read())
writer.writelines(httpresponse)
elif os.path.isdir(relative_path): # homepage
httpresponse = [b'HTTP/1.0 200 OK\r\n',
b'Content-Type:text/html; charset=utf-8\r\n',
b'Connection: close\r\n', b'\r\n',
bytes('<html><head><title>Index of ' + relative_path + '</title></head><body>\r\n',
encoding="UTF-8"),
bytes('<div style="position:relative;"><img src="https://www.sustech.edu.cn/wp-content/uploads/%E6%A0%A1%E5%8E%86-1.jpg?id=8835261" width="100%"height="30%"></div><div style="position:absolute; left:80px; top:50px; "><img src="https://www.sustech.edu.cn/wp-content/themes/twentyseventeen/images/sustech-logo-cn.png" width="433" height="86"/></div>',encoding='UTF-8'),
bytes('<h1>Index of ' + relative_path + '</h1><hr><ul>\r\n', encoding='UTF-8'),
bytes('<li><a href="../">..</a></li><br>\r\n', encoding='UTF-8')]
dirlist = os.listdir(relative_path)
print(dirlist)
for i in range(len(dirlist)):
if os.path.isfile(dirlist[i]): # file
httpresponse.append(
bytes('<li><a href="' + dirlist[i] + '">' + dirlist[i] + "</a></li><br>\r\n",
encoding='UTF-8'))
else: # directory
httpresponse.append(
bytes('<li><a href="' + dirlist[i] + '/">' + dirlist[i] + '</a></li><br>\r\n',
encoding='UTF-8'))
httpresponse.append(bytes('</ul><hr></body></html>', encoding='UTF-8'))
writer.writelines(httpresponse)
else: # Not existing file or directory returns 404 response.
writer.writelines([
b'HTTP/1.0 404 Method Not Found\r\n',
b'Content-Type:text/html; charset=utf-8\r\n',
b'Connection: close\r\n',
b'\r\n',
html404,
b'\r\n'
])
await writer.drain()
writer.close()
elif message[0] == "HEAD": # Method HEAD returns 200 response and the head of http response if such dir exists .
dir_path = urllib.parse.unquote(message[1])
if dir_path[0] != '/':
dir_path = '/' + dir_path[0]
relative_path = root_dir + dir_path
print(relative_path)
if os.path.isfile(relative_path): # Add mimetype to the http header if the path is a file.
mimetype = mimetypes.guess_type(relative_path)[0]
if mimetype is None:
mimetype = 'application/octet-stream'
httpresponse = [b'HTTP/1.0 200 OK\r\n',
bytes('Content-Type:', encoding='UTF-8') +
bytes(mimetype + '; charset=utf-8\r\n', encoding='UTF-8'),
bytes('Content-Length: ' + str(os.path.getsize(relative_path)) + '\r\n',
encoding="UTF-8"),
b'\r\n']
writer.writelines(httpresponse)
elif os.path.isdir(relative_path): # Edit the http header of a directory
httpresponse = [b'HTTP/1.0 200 OK\r\n',
b'Content-Type:text/html; charset=utf-8\r\n',
b'Connection: close\r\n', b'\r\n',
]
writer.writelines(httpresponse)
else: # Not existing file or directory returns 404 response.
writer.writelines([
b'HTTP/1.0 404 Method Not Found\r\n',
b'Content-Type:text/html; charset=utf-8\r\n',
b'Connection: close\r\n',
b'\r\n',
html404,
b'\r\n'
])
await writer.drain()
writer.close()
elif message[0] == "POST": # Method POST returns 405 response.
writer.writelines([
b'HTTP/1.0 405 Method Not Allowed\r\n',
b'Content-Type:text/html; charset=utf-8\r\n',
b'Connection: close\r\n',
b'\r\n',
html405,
b'\r\n'
])
await writer.drain()
writer.close()
else: # Other method returns 405 response.
writer.writelines([
b'HTTP/1.0 405 Method Not Allowed\r\n',
b'Content-Type:text/html; charset=utf-8\r\n',
b'Connection: close\r\n',
b'\r\n',
html405,
b'\r\n'
])
await writer.drain()
writer.close()
except ConnectionError:
pass
if __name__ == '__main__':
# python FileBrowser.py --port 8000 --dir ~/
parser = argparse.ArgumentParser(description='Simple Web File Browser')
parser.add_argument('--port', type=int, default=8080,
help='an integer for the port of the simple web file browser')
parser.add_argument('--dir', type=str, default="./",
help='The Directory that the browser should display for home page')
args = parser.parse_args()
loop = asyncio.get_event_loop()
coro = asyncio.start_server(dispatch, '127.0.0.1', port=args.port, loop=loop)
server = loop.run_until_complete(coro)
root_dir = args.dir
if root_dir[-1] == '/':
root_dir = root_dir[:-1]
print("server run at port %d" % args.port)
print("server run at dir %s" % args.dir)
print('Serving on {} and Hit Ctrl + C to end the server'.format(server.sockets[0].getsockname()))
try:
loop.run_forever()
except KeyboardInterrupt:
exit()
server.close()
loop.run_until_complete(server.wait_closed())
loop.close()
- 在terminal中运行py文件,指定端口和路径:
python3 FileBrowser.py --port 8080 --dir ~/
- 实例:
- 打开浏览器看看效果:(自己随便加了个图片)
已知MIME TYPE 打开文件并以HTML文件展示在浏览器,例如打开FileBrowser.py
未知MIME Type 会下载到本地:
进入文件夹后可以通过..
返回上级目录:
访问不存在的文件,如在地址栏中输入URL:
则会报出404页面:
HEAD 方法测试:
POST 返回405:
二、emm
cmd 输入 python -m http.server
就可以实现当前文件夹的file browser。
例如:
- emm,这不啥也有了吗(404 也有。。。)
三、总结
- 需要了解HTTP报文结构:
结构其实都是:
注意blank line 和 crlf 都不可以少。(\r\n)
- 需要自学点HTML(Message body内部)