【python】如何通过Python中的http.server搭建文件上传下载服务

✨✨ 欢迎大家来到景天科技苑✨✨

🎈🎈 养成好习惯,先赞后看哦~🎈🎈

🏆 作者简介:景天科技苑

🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。

🏆《博客》:Python全栈,PyQt5和Tkinter桌面开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi,flask等框架,云原生K8S,linux,shell脚本等实操经验,网站搭建,数据库等分享。

所属的专栏: python综合应用,基础语法到高阶实战教学
景天的主页: 景天科技苑

文章目录

  • Python搭建文件下载服务
    • 一、概述
      • [1.1 准备工作](#1.1 准备工作)
      • [1.2 场景描述](#1.2 场景描述)
    • 二、搭建HTTP服务器
      • [2.1 基础服务器搭建](#2.1 基础服务器搭建)
        • [2.1.1 示例代码](#2.1.1 示例代码)
        • [2.1.2 运行服务器](#2.1.2 运行服务器)
      • [2.2 自定义文件目录](#2.2 自定义文件目录)
        • [2.2.1 示例代码](#2.2.1 示例代码)
        • [2.2.2 运行服务器](#2.2.2 运行服务器)
      • [2.3 上传下载服务](#2.3 上传下载服务)
        • [2.3.1 示例代码](#2.3.1 示例代码)
        • [2.3.2 运行服务器](#2.3.2 运行服务器)
    • 三、安全性考虑
    • 四、总结

Python搭建文件下载服务

在Web开发中,文件下载服务是一个常见且基础的功能。Python的http.server模块提供了一个简单而强大的方式来搭建HTTP服务器,进而实现文件下载服务。本文将结合实际案例,详细介绍如何使用Python的http.server模块来搭建文件下载服务。

一、概述

Python的http.server模块是Python标准库的一部分,它提供了一个基本的HTTP服务器类和请求处理器。通过这个模块,我们可以轻松地搭建一个HTTP服务器,用于处理客户端的HTTP请求,包括文件下载请求。

1.1 准备工作

在开始之前,请确保你的计算机上已安装Python环境。Python 3.x版本已经内置了http.server模块,因此你不需要额外安装任何库。

1.2 场景描述

假设我们有一个需求:需要搭建一个HTTP服务器,用于提供特定目录下的文件下载服务。客户端通过访问服务器上的特定URL,即可下载服务器上的文件。

二、搭建HTTP服务器

2.1 基础服务器搭建

首先,我们将使用http.server模块搭建一个基础的HTTP服务器。

2.1.1 示例代码
python 复制代码
import http.server
import socketserver

# 设置端口号
PORT = 8000

# 创建一个TCP服务器
with socketserver.TCPServer(("", PORT), http.server.SimpleHTTPRequestHandler) as httpd:
    print("Serving at port", PORT)
    # 启动服务器,使其一直运行
    httpd.serve_forever()

在上述代码中,我们导入了http.serversocketserver模块,并设置了服务器监听的端口号为8000。然后,我们创建了一个TCPServer实例,并将http.server.SimpleHTTPRequestHandler作为请求处理器传入。最后,我们调用serve_forever()方法启动服务器,使其持续运行。

2.1.2 运行服务器

将上述代码保存为一个.py文件,例如server.py。然后,在命令行中导航到该文件所在的目录,并执行以下命令:

bash 复制代码
python server.py

执行后,你将在控制台看到"Serving at port 8000"的提示,表示服务器已成功启动。

2.2 自定义文件目录

默认情况下,SimpleHTTPRequestHandler会处理服务器当前工作目录下的文件。然而,我们可能希望服务器处理特定目录下的文件。为了实现这一需求,我们可以创建一个继承自SimpleHTTPRequestHandler的类,并重写translate_path方法。

2.2.1 示例代码
python 复制代码
import http.server
import socketserver
import os

# 定义服务器端口
PORT = 8080
# 定义文件目录
DIRECTORY = "/path/to/your/directory"

# 创建一个处理请求的类
class MyHandler(http.server.SimpleHTTPRequestHandler):
    def translate_path(self, path):
        # 确保返回的路径在指定的目录内
        path = os.path.normpath(os.path.join(DIRECTORY, path.lstrip('/')))
        return path

# 创建服务器
with socketserver.TCPServer(("", PORT), MyHandler) as httpd:
    print(f"Serving at port {PORT}")
    # 启动服务器,使其一直运行
    httpd.serve_forever()

在上述代码中,我们定义了DIRECTORY变量来指定文件所在的目录,并创建了一个MyHandler类,该类继承自SimpleHTTPRequestHandler并重写了translate_path方法。在translate_path方法中,我们通过os.path.joinos.path.normpath函数将请求的路径与DIRECTORY变量拼接起来,并确保返回的路径在指定的目录内。

2.2.2 运行服务器

将上述代码保存为一个.py文件,并替换DIRECTORY变量的值为你希望服务器处理的文件目录。然后,按照2.1.2节中的步骤运行服务器。

2.3 上传下载服务

有时,我们可能需要定制HTTP响应头,以满足特定的需求。

2.3.1 示例代码

为了定制响应头,我们继续上面的例子,通过重写do_GET方法来设置特定的HTTP响应头,比如Content-Disposition以支持文件下载。

python 复制代码
import os
import cgi
from http.server import HTTPServer, BaseHTTPRequestHandler
from urllib.parse import unquote


class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        # Parse the URL to get the file path
        file_path = unquote(self.path.strip("/"))

        if os.path.isfile(file_path):
            # If the requested path is a file, serve the file as an attachment
            self.send_response(200)
            self.send_header('Content-Type', 'application/octet-stream')
            # self.send_header('Content-Disposition', f'attachment; filename="{os.path.basename(file_path)}"')
            self.end_headers()
            with open(file_path, 'rb') as file:
                self.wfile.write(file.read())
        else:
            # Otherwise, show the upload form and file list
            self.send_response(200)
            self.send_header('Content-type', 'text/html')
            self.end_headers()

            # List files in the current directory
            file_list = os.listdir('.')
            file_list_html = ''.join(f'<li><a href="/{file}">{file}</a></li>' for file in file_list)

            self.wfile.write(f'''
                <html>
                <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
                <body>
                    <form enctype="multipart/form-data" method="post">
                        <input type="file" name="file">
                        <input type="submit" value="Upload">
                    </form>
                    <h2>Files in current directory:</h2>
                    <ul>
                        {file_list_html}
                    </ul>
                </body>
                </html>
            '''.encode())

    def do_POST(self):
        try:
            content_type = self.headers['Content-Type']
            if 'multipart/form-data' in content_type:
                form = cgi.FieldStorage(
                    fp=self.rfile,
                    headers=self.headers,
                    environ={'REQUEST_METHOD': 'POST'}
                )
                file_field = form['file']
                if file_field.filename:
                    # Save the file
                    with open(os.path.join('.', file_field.filename), 'wb') as f:
                        f.write(file_field.file.read())
                    self.send_response(200)
                    self.send_header('Content-type', 'text/html')
                    self.end_headers()
                    self.wfile.write(b'''
                        <html>
                        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
                        <body>
                            <p>File uploaded successfully.</p>
                            <script>
                                setTimeout(function() {
                                    window.location.href = "/";
                                }, 2000);
                            </script>
                        </body>
                        </html>
                    ''')
                else:
                    self.send_response(400)
                    self.send_header('Content-type', 'text/html')
                    self.end_headers()
                    self.wfile.write(b'''
                        <html>
                        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
                        <body>
                            <p>No file uploaded.</p>
                            <script>
                                setTimeout(function() {
                                    window.location.href = "/";
                                }, 2000);
                            </script>
                        </body>
                        </html>
                    ''')
            else:
                self.send_response(400)
                self.send_header('Content-type', 'text/html')
                self.end_headers()
                self.wfile.write(b'''
                    <html>
                    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
                    <body>
                        <p>Invalid content type.</p>
                        <script>
                            setTimeout(function() {
                                window.location.href = "/";
                            }, 2000);
                        </script>
                    </body>
                    </html>
                ''')
        except Exception as e:
            self.send_response(500)
            self.send_header('Content-type', 'text/html')
            self.end_headers()
            self.wfile.write(f'''
                <html>
                <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
                <body>
                    <p>Error occurred: {str(e)}</p>
                    <script>
                        setTimeout(function() {{
                            window.location.href = "/";
                        }}, 2000);
                    </script>
                </body>
                </html>
            '''.encode())


def run(server_class=HTTPServer, handler_class=SimpleHTTPRequestHandler, port=8000):
    server_address = ('', port)
    httpd = server_class(server_address, handler_class)

    # Uncomment the following lines if you need HTTPS support
    # httpd.socket = ssl.wrap_socket(httpd.socket,
    #                                keyfile="path/to/key.pem",
    #                                certfile='path/to/cert.pem', server_side=True)

    print(f'Starting httpd server on port {port}...')
    httpd.serve_forever()


if __name__ == '__main__':
    run()

在上述代码中,CustomFileHandler类继承自之前定义的MyHandler类(或直接从http.server.SimpleHTTPRequestHandler继承,如果你没有重写translate_path方法的话)。我们重写了do_GET方法,以处理GET请求并定制响应头。

  • 首先,我们通过translate_path方法获取请求文件的实际路径。
  • 然后,我们检查该文件是否存在。
  • 如果文件存在,我们使用mimetypes.guess_type函数猜测文件的MIME类型,并设置相应的Content-type响应头。
  • 最后,我们打开文件,并使用copyfile方法将文件内容发送给客户端。
    如果文件不存在,我们通过send_error方法返回404错误。

浏览器访问http://localhost:8000/

能看到下载列表

点击选择文件可以上传

2.3.2 运行服务器

将上述代码保存为一个.py文件,并替换DIRECTORY变量的值为你希望服务器处理的文件目录。然后,按照之前的步骤运行服务器。

现在,当你通过浏览器访问服务器上的文件时(例如,http://localhost:8080/example.pdf),浏览器将以下载的方式处理该文件,而不是尝试在浏览器中打开它。

三、安全性考虑

虽然http.server模块提供了一个快速搭建HTTP服务器的方法,但它在安全性方面存在一些局限性。例如,默认情况下,它会处理服务器上所有可读文件的请求,这可能会暴露敏感信息。

为了增强安全性,你可以:

  • 限制服务器的访问权限,例如,使用防火墙规则来限制只有特定的IP地址或网络才能访问服务器。
  • 创建一个白名单,仅允许访问白名单中指定的文件或目录。
  • 使用更安全的HTTP服务器框架,如Flask或Django,这些框架提供了更丰富的功能和更好的安全性支持。

四、总结

通过本教程,我们学习了如何使用Python的http.server模块搭建一个基本的HTTP服务器,并实现文件下载服务。我们介绍了如何设置服务器端口、自定义文件目录、定制HTTP响应头以及处理GET请求。最后,我们还讨论了使用http.server模块时需要注意的一些安全性问题。希望这些内容对你有所帮助!

相关推荐
databook2 小时前
Manim实现闪光轨迹特效
后端·python·动效
Juchecar3 小时前
解惑:NumPy 中 ndarray.ndim 到底是什么?
python
用户8356290780513 小时前
Python 删除 Excel 工作表中的空白行列
后端·python
Json_3 小时前
使用python-fastApi框架开发一个学校宿舍管理系统-前后端分离项目
后端·python·fastapi
数据智能老司机10 小时前
精通 Python 设计模式——分布式系统模式
python·设计模式·架构
数据智能老司机11 小时前
精通 Python 设计模式——并发与异步模式
python·设计模式·编程语言
数据智能老司机11 小时前
精通 Python 设计模式——测试模式
python·设计模式·架构
数据智能老司机11 小时前
精通 Python 设计模式——性能模式
python·设计模式·架构
c8i11 小时前
drf初步梳理
python·django
每日AI新事件11 小时前
python的异步函数
python