这段代码实现了一个简单的文件上传和下载服务器,使用 http.server
和 socketserver
模块构建了一个 HTTP 服务器,并支持文件的上传和下载。代码可以直接粘贴使用。无需改动文件会上传到该py文件的当前目录下的uploads目录下,可以指定别的目录(自行修改代码)
代码解释与注释
import http.server
import socketserver
import cgi
import os
import argparse
# 默认上传目录
UPLOAD_DIR = 'uploads'
os.makedirs(UPLOAD_DIR, exist_ok=True) # 确保上传目录存在,如果不存在则创建
class CustomHTTPRequestHandler(http.server.SimpleHTTPRequestHandler):
def do_GET(self):
# 处理 GET 请求
if self.path == '/':
# 处理根路径请求,返回 HTML 页面
self.send_response(200)
self.send_header('Content-type', 'text/html; charset=UTF-8') # 设置内容类型为 HTML,并指定 UTF-8 编码
self.end_headers()
# 获取上传目录中的文件列表
file_list = os.listdir(UPLOAD_DIR)
# 生成文件列表的 HTML
file_list_html = '<ul>'
for filename in file_list:
file_list_html += f'<li>{filename}</li>'
file_list_html += '</ul>'
# 返回文件上传和下载表单,以及文件列表
self.wfile.write(f'''
<html>
<meta charset="UTF-8">
<title>文件 上传/下载</title>
<body>
<h1>上传文件</h1>
<form id="uploadForm" enctype="multipart/form-data" method="POST">
<input type="file" name="file" multiple οnchange="toggleUploadButton()">
<button type="submit" id="uploadButton" disabled>Upload</button>
</form>
<h1>上传目录已有文件</h1>
<div id="fileList">
{file_list_html}
</div>
<h1>根据文件全称下载文件</h1>
<form method="GET" action="/download">
<input type="text" name="filename" placeholder="Enter filename">
<input type="submit" value="Download">
</form>
<script>
function toggleUploadButton() {{
// 根据文件选择框中的文件数量来启用或禁用上传按钮
var fileInput = document.querySelector('input[type="file"]');
var uploadButton = document.getElementById('uploadButton');
uploadButton.disabled = fileInput.files.length === 0;
}}
// 监听表单提交事件
document.getElementById('uploadForm').onsubmit = function(event) {{
event.preventDefault(); // 阻止表单默认提交行为
// 创建 FormData 对象
var formData = new FormData(this);
var xhr = new XMLHttpRequest();
xhr.open('POST', '/', true);
xhr.onload = function() {{
if (xhr.status === 200) {{
alert('Files uploaded successfully');
document.querySelector('input[type="file"]').value = ''; // 清空文件选择框
toggleUploadButton(); // 重新禁用上传按钮
updateFileList(); // 更新文件列表
}} else {{
alert('Error uploading files');
}}
}};
xhr.send(formData);
}};
// 更新文件列表
function updateFileList() {{
var xhr = new XMLHttpRequest();
xhr.open('GET', '/filelist', true);
xhr.onload = function() {{
if (xhr.status === 200) {{
document.getElementById('fileList').innerHTML = xhr.responseText;
}} else {{
alert('Error fetching file list');
}}
}};
xhr.send();
}}
</script>
</body>
</html>
'''.encode('utf-8')) # 使用 UTF-8 编码返回 HTML 内容
elif self.path.startswith('/download'):
# 处理下载请求
filename = self.path.split('filename=')[-1]
filepath = os.path.join(UPLOAD_DIR, filename)
if os.path.exists(filepath):
self.send_response(200)
self.send_header('Content-Disposition', f'attachment; filename="{filename}"')
self.end_headers()
with open(filepath, 'rb') as file:
self.wfile.write(file.read()) # 发送文件内容
else:
self.send_response(404)
self.end_headers()
self.wfile.write(b'File not found') # 文件未找到
elif self.path == '/filelist':
# 处理文件列表请求
file_list = os.listdir(UPLOAD_DIR)
file_list_html = '<ul>'
for filename in file_list:
file_list_html += f'<li>{filename}</li>'
file_list_html += '</ul>'
self.send_response(200)
self.send_header('Content-type', 'text/html; charset=UTF-8') # 设置内容类型为 HTML,并指定 UTF-8 编码
self.end_headers()
self.wfile.write(file_list_html.encode('utf-8')) # 返回文件列表的 HTML 内容
def do_POST(self):
# 处理 POST 请求
if self.path == '/':
# 使用 cgi.FieldStorage() 处理表单数据
form = cgi.FieldStorage(fp=self.rfile, headers=self.headers, environ={'REQUEST_METHOD': 'POST'})
if 'file' in form:
# 处理上传的文件
files = form['file']
if not isinstance(files, list):
files = [files]
for file_field in files:
if file_field.filename:
file_name = os.path.basename(file_field.filename) # 获取原始文件名
file_data = file_field.file.read() # 读取文件数据
# 保存文件
file_path = os.path.join(UPLOAD_DIR, file_name)
with open(file_path, 'wb') as f:
f.write(file_data)
# 返回成功响应
self.send_response(200)
self.end_headers()
self.wfile.write(b'Files uploaded successfully')
else:
self.send_response(400)
self.end_headers()
self.wfile.write(b'No files uploaded') # 没有上传文件
def run_server(port):
# 启动 HTTP 服务器
Handler = CustomHTTPRequestHandler
with socketserver.TCPServer(("", port), Handler) as httpd:
print(f"Serving at port {port}")
httpd.serve_forever()
if __name__ == '__main__':
# 使用 argparse 来解析命令行参数
parser = argparse.ArgumentParser(description="Simple file upload/download server")
parser.add_argument('--port', type=int, default=8000, help='Port to serve on (default: 8000)')
args = parser.parse_args()
# 启动服务器
run_server(args.port)
主要功能:
-
处理 GET 请求:
- 根路径
/
:显示文件上传表单、当前文件列表和文件下载表单。 /download
:根据文件名下载指定文件。/filelist
:返回当前上传目录中的文件列表。
- 根路径
-
处理 POST 请求:
- 根路径
/
:处理文件上传,将文件保存到指定目录。
- 根路径
-
JavaScript 代码:
- 处理文件选择变化,启用或禁用上传按钮。
- 使用 AJAX 提交文件上传表单并更新文件列表。
-
服务器启动:
- 使用
argparse
解析命令行参数,允许通过--port
参数指定端口,默认端口为 8000。
- 使用
使用说明:
-
启动服务器:
- 运行
python <your_script_name>.py
。 - 可以使用
--port
参数指定端口,例如python <your_script_name>.py --port 8080
。
- 运行
-
访问网页:
- 打开浏览器并访问
http://127.0.0.1:8000/
(或指定的端口)。
- 打开浏览器并访问
-
上传文件:
- 选择文件并点击“Upload”按钮。
- 上传成功后,文件会自动显示在文件列表中。
-
下载文件:
- 输入文件名并点击“Download”按钮来下载文件。