在日常开发或测试中,我们经常需要一个简单的 HTTP 服务器来共享文件或提供静态资源。Python 的 http.server 模块是一个不错的选择,但它默认只支持 HTTP。本文将介绍如何基于 http.server 快速搭建一个功能更强大的 HTTPS 文件服务器,它不仅安全,还支持跨域请求(CORS)和大文件断点续传(Range Requests)。
核心功能
-
HTTPS 加密 :通过
mkcert工具自动生成和管理本地信任的 SSL/TLS 证书。 -
跨域资源共享 (CORS):允许来自不同域的前端应用无缝访问。
-
断点续传 :支持
Range请求头,实现大文件的分段下载和断点续传。 -
简单易用:一键启动,自动处理证书生成。
准备工作
首先,你需要安装 mkcert 工具来生成本地证书。mkcert 是一个简单的工具,用于制作本地信任的开发证书。
在 Linux 上:
# 例如,在 Ubuntu/Debian 上
sudo apt update && sudo apt install libnss3-tools
curl -JLO "https://dl.filippo.io/mkcert/latest?for=linux/amd64"
chmod +x mkcert-v*-linux-amd64
sudo cp mkcert-v*-linux-amd64 /usr/local/bin/mkcert
在 macOS 上:
brew install mkcert
在 Windows 上: 可以通过 Chocolatey 或从 官方 releases 下载。
安装后,在你的终端运行一次 mkcert -install,它会在你的系统信任存储中安装一个本地 CA。
Python 实现代码
将以下代码保存为 https_server.py。
python
import http.server
import ssl
import os
import subprocess
import re
# --- 配置区 ---
# 证书和密钥文件所在目录
CERT_DIR = "./.cert"
# 要生成证书的主机名和IP列表
MKCERT_HOSTS = ["localhost", "127.0.0.1", "::1", "192.168.1.100"] # 修改为你的IP
# HTTPS 服务监听地址和端口
HOST = "0.0.0.0"
PORT = 8443
# 文件服务的根目录
ROOT_DIR = "./shared_files" # 修改为你的文件目录
# ==================== 1. 通过 mkcert 自动生成/复用证书 ====================
def ensure_certificate():
"""确保存在 mkcert 生成的证书,缺失时自动调用 mkcert。"""
cert_file = os.path.join(CERT_DIR, "server.crt")
key_file = os.path.join(CERT_DIR, "server.key")
os.makedirs(CERT_DIR, exist_ok=True)
if os.path.exists(cert_file) and os.path.exists(key_file):
print(f"发现已有证书:{cert_file},直接复用。")
return cert_file, key_file
print("未检测到证书,正在调用 mkcert 生成...")
mkcert_cmd = ["mkcert", "-key-file", key_file, "-cert-file", cert_file, *MKCERT_HOSTS]
try:
subprocess.run(mkcert_cmd, check=True, capture_output=True, text=True)
except FileNotFoundError:
print("错误:未找到 mkcert 命令。请先安装 mkcert 并执行 'mkcert -install'。")
raise
except subprocess.CalledProcessError as exc:
print(f"错误:mkcert 生成证书失败: {exc.stderr}")
raise
print("证书生成完成。")
return cert_file, key_file
# ==================== 2. 自定义 HTTPS 请求处理器 (支持 CORS 和 Range) ====================
class AdvancedHTTPRequestHandler(http.server.SimpleHTTPRequestHandler):
"""
一个增强版的请求处理器,添加了:
- CORS 支持
- Range 请求支持 (断点续传)
"""
def end_headers(self):
"""在发送响应头之前,添加 CORS 相关的头。"""
self.send_header('Access-Control-Allow-Origin', '*')
self.send_header('Access-Control-Allow-Methods', 'GET, HEAD, OPTIONS')
self.send_header('Access-Control-Allow-Headers', '*')
self.send_header('Access-Control-Max-Age', '86400') # 预检请求结果缓存24小时
super().end_headers()
def do_OPTIONS(self):
"""处理预检请求(Preflight Request)。"""
self.send_response(200)
self.end_headers()
def do_GET(self):
"""重写 GET 方法以支持 Range 请求。"""
range_header = self.headers.get('Range')
if range_header:
# 尝试解析 Range 头
match = re.match(r'bytes=(\d+)-(\d*)', range_header)
if match:
start_str, end_str = match.groups()
start = int(start_str)
end = int(end_str) if end_str else None
path = self.translate_path(self.path)
if os.path.isfile(path):
file_size = os.path.getsize(path)
# 验证并调整范围
if start >= file_size:
self.send_error(416, "Requested Range Not Satisfiable")
return
end = min(end, file_size - 1) if end is not None else file_size - 1
# 发送 206 Partial Content 响应
self.send_response(206)
self.send_header('Content-Type', self.guess_type(path))
self.send_header('Content-Length', str(end - start + 1))
self.send_header('Content-Range', f'bytes {start}-{end}/{file_size}')
self.send_header('Accept-Ranges', 'bytes')
self.end_headers()
# 发送指定范围的文件内容
with open(path, 'rb') as f:
f.seek(start)
remaining = end - start + 1
chunk_size = 8192
while remaining > 0:
chunk = f.read(min(chunk_size, remaining))
if not chunk:
break
self.wfile.write(chunk)
remaining -= len(chunk)
return
# 如果不是 Range 请求或解析失败,使用默认的 GET 处理逻辑
super().do_GET()
def send_head(self):
"""重写 send_head 以确保在正常响应中也包含 Accept-Ranges 头。"""
path = self.translate_path(self.path)
f = None
if os.path.isdir(path):
# 对于目录,如果没有 index 文件,则返回 403 Forbidden 而不是列表
# 你可以根据需要修改此行为
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_path = os.path.join(path, index)
if os.path.exists(index_path):
path = index_path
break
else:
self.send_error(403, "Directory listing not allowed")
return None
ctype = self.guess_type(path)
try:
f = open(path, 'rb')
except OSError:
self.send_error(404, "File not found")
return None
try:
fs = os.fstat(f.fileno())
self.send_response(200)
self.send_header("Content-type", ctype)
self.send_header("Content-Length", str(fs[6]))
self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
self.send_header("Accept-Ranges", "bytes") # 关键:告知客户端支持断点续传
self.end_headers()
return f
except:
f.close()
raise
# ==================== 3. 启动服务 ====================
if __name__ == "__main__":
# 确保证书存在
cert_file, key_file = ensure_certificate()
# 确保文件根目录存在
os.makedirs(ROOT_DIR, exist_ok=True)
print(f"文件服务根目录设置为: {os.path.abspath(ROOT_DIR)}")
# 切换工作目录到文件根目录
os.chdir(ROOT_DIR)
# 创建 HTTP 服务器实例
server = http.server.HTTPServer((HOST, PORT), AdvancedHTTPRequestHandler)
# 包装 SSL 上下文
ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
ssl_context.load_cert_chain(certfile=cert_file, keyfile=key_file)
server.socket = ssl_context.wrap_socket(server.socket, server_side=True)
print(f"\nHTTPS 文件服务器已启动!")
print(f"服务地址: https://{HOST}:{PORT}")
print(f"提示: 将需要共享的文件放入 '{os.path.abspath(ROOT_DIR)}' 目录。")
print(f"按 Ctrl+C 停止服务。")
try:
server.serve_forever()
except KeyboardInterrupt:
print("\n正在关闭服务...")
server.shutdown()
print("服务已关闭。")
如何使用
-
保存代码 :将上面的代码保存为
https_server.py。 -
修改配置 :打开
https_server.py文件,修改MKCERT_HOSTS、PORT和ROOT_DIR等配置项以符合你的需求。特别是MKCERT_HOSTS,建议至少包含你的局域网 IP 地址,这样局域网内的其他设备也可以访问。 -
准备文件 :创建
shared_files目录(或你在配置中指定的ROOT_DIR),并将需要共享的文件放入其中。 -
运行服务器:
python3 https_server.py -
访问文件 :在浏览器或任何支持 HTTPS 的客户端中访问
https://<你的IP或localhost>:8443/文件名。由于证书是本地信任的,浏览器不会显示安全警告。
代码亮点解析
-
自动证书管理 (
ensure_certificate) :脚本会自动检查证书是否存在,如果不存在,会调用mkcert命令生成。这简化了初始设置。 -
CORS 支持 (
end_headers和do_OPTIONS) :通过重写end_headers方法,在每个响应中都加入 CORS 头,允许来自任何域的请求。do_OPTIONS方法则正确响应预检请求。 -
断点续传 (
do_GET) :通过解析Range请求头,并以206 Partial Content状态码和Content-Range头进行响应,实现了断点续传功能,这对于下载大文件至关重要。 -
清晰的结构:代码被分成了证书准备、请求处理和服务启动三个主要部分,易于理解和维护。
这个小小的 Python 脚本提供了一个安全、强大且易于部署的文件共享解决方案,非常适合开发和测试环境使用。