python HTTP Server 文件上传与下载

2023-11-10

实现在局域网(同一WIFI下) 文件上传与下载

该模块通过实现标准GET在BaseHTTPServer上构建
和HEAD请求。(将所有代码粘贴到同一个py文件中,即可使用)

所需包

基于python3版本实现,python2版本无涉猎

import os
import sys
import argparse
import posixpath

try:
    from html import escape
except ImportError:
    from cgi import escape
import shutil
import mimetypes
import re
import signal
from io import StringIO, BytesIO

if sys.version_info.major == 3:
    # Python3
    from urllib.parse import quote
    from urllib.parse import unquote
    from http.server import HTTPServer
    from http.server import BaseHTTPRequestHandler

基本类 简单HTTP服务类

带有GET/HEAD/POST命令的简单HTTP请求处理程序。

这将提供当前目录中的文件及其子目录。
文件的MIME类型由调用.gues_type()方法。
并且可以接收上传的文件由客户提供。

GET/HEAD/POST请求是相同的,除了HEAD请求忽略文件的实际内容。

class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
    server_version = "simple_http_server/" + __version__

    def do_GET(self):
        """Serve a GET request."""
        fd = self.send_head()
        if fd:
            shutil.copyfileobj(fd, self.wfile)
            fd.close()

    def do_HEAD(self):
        """Serve a HEAD request."""
        fd = self.send_head()
        if fd:
            fd.close()

    def do_POST(self):
        """Serve a POST request."""
        r, info = self.deal_post_data()
        print(r, info, "by: ", self.client_address)
        f = BytesIO()
        f.write(b'<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">')
        f.write(b"<html>\n<title>Upload Result Page</title>\n")
        f.write(b"<body>\n<h2>Upload Result Page</h2>\n")
        f.write(b"<hr>\n")
        if r:
            f.write(b"<strong>Success:</strong>")
        else:
            f.write(b"<strong>Failed:</strong>")
        f.write(info.encode('utf-8'))
        f.write(b"<br><a href=\".\">back</a>")
        f.write(b"<hr><small>Powered By: freelamb, check new version at ")
        # 原始代码地址 可以参考
        f.write(b"<a href=\"https://github.com/freelamb/simple_http_server\">")
        f.write(b"here</a>.</small></body>\n</html>\n")
        length = f.tell()
        f.seek(0)
        self.send_response(200)
        self.send_header("Content-type", "text/html;charset=utf-8")
        self.send_header("Content-Length", str(length))
        self.end_headers()
        if f:
            shutil.copyfileobj(f, self.wfile)
            f.close()

    def deal_post_data(self):
        boundary = self.headers["Content-Type"].split("=")[1].encode('utf-8')
        remain_bytes = int(self.headers['content-length'])
        line = self.rfile.readline()
        remain_bytes -= len(line)
        if boundary not in line:
            return False, "Content NOT begin with boundary"
        line = self.rfile.readline()
        remain_bytes -= len(line)
        fn = re.findall(r'Content-Disposition.*name="file"; filename="(.*)"', line.decode('utf-8'))
        if not fn:
            return False, "Can't find out file name..."
        path = translate_path(self.path)
        fn = os.path.join(path, fn[0])
        while os.path.exists(fn):
            fn += "_"
        line = self.rfile.readline()
        remain_bytes -= len(line)
        line = self.rfile.readline()
        remain_bytes -= len(line)
        try:
            out = open(fn, 'wb')
        except IOError:
            return False, "Can't create file to write, do you have permission to write?"

        pre_line = self.rfile.readline()
        remain_bytes -= len(pre_line)
        while remain_bytes > 0:
            line = self.rfile.readline()
            remain_bytes -= len(line)
            if boundary in line:
                pre_line = pre_line[0:-1]
                if pre_line.endswith(b'\r'):
                    pre_line = pre_line[0:-1]
                out.write(pre_line)
                out.close()
                return True, "File '%s' upload success!" % fn
            else:
                out.write(pre_line)
                pre_line = line
        return False, "Unexpect Ends of data."

    def send_head(self):
        """
        GET和HEAD命令的通用代码。
		这将发送响应代码和MIME标头。
		返回值要么是文件对象
		(除非命令是HEAD,否则调用方必须将其复制到输出文件中,
		并且在任何情况下都必须由调用方关闭),
		要么是None,在这种情况下,调用方无需进一步操作。
        """
        path = translate_path(self.path)
        if os.path.isdir(path):
            if not self.path.endswith('/'):
                # 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 = os.path.join(path, index)
                if os.path.exists(index):
                    path = index
                    break
            else:
                return self.list_directory(path)
        content_type = self.guess_type(path)
        try:
			#始终以二进制模式读取。以文本模式打开文件可能会导致
			#换行翻译,使内容的实际大小
			#传输*小于*内容长度!
            f = open(path, 'rb')
        except IOError:
            self.send_error(404, "File not found")
            return None
        self.send_response(200)
        self.send_header("Content-type", content_type)
        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

    def list_directory(self, path):
        """
        帮助程序生成目录列表(缺少index.html)。
		返回值为file对象或None(表示错误)。
		无论哪种情况,都会发送标头接口与send_head()相同。
        """
        try:
            list_dir = os.listdir(path)
        except os.error:
            self.send_error(404, "No permission to list directory")
            return None
        list_dir.sort(key=lambda a: a.lower())
        f = BytesIO()
        display_path = escape(unquote(self.path))
        f.write(b'<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">')
        f.write(b"<html>\n<title>Directory listing for %s</title>\n" % display_path.encode('utf-8'))
        f.write(b"<body>\n<h2>Directory listing for %s</h2>\n" % display_path.encode('utf-8'))
        f.write(b"<hr>\n")
        f.write(b"<form ENCTYPE=\"multipart/form-data\" method=\"post\">")
        f.write(b"<input name=\"file\" type=\"file\"/>")
        f.write(b"<input type=\"submit\" value=\"upload\"/></form>\n")
        f.write(b"<hr>\n<ul>\n")
        for name in list_dir:
            fullname = os.path.join(path, name)
            display_name = linkname = name
            # Append / for directories or @ for symbolic links
            if os.path.isdir(fullname):
                display_name = name + "/"
                linkname = name + "/"
            if os.path.islink(fullname):
                display_name = name + "@"
                # Note: a link to a directory displays with @ and links with /
            f.write(
                b'<li><a href="%s">%s</a>\n' % (quote(linkname).encode('utf-8'), escape(display_name).encode('utf-8')))
        f.write(b"</ul>\n<hr>\n</body>\n</html>\n")
        length = f.tell()
        f.seek(0)
        self.send_response(200)
        self.send_header("Content-type", "text/html;charset=utf-8")
        self.send_header("Content-Length", str(length))
        self.end_headers()
        return f

    def guess_type(self, path):
        """
        参数是PATH(文件名)。
		返回值是表单类型/子类型的字符串,
		可用于MIME内容类型标头。
		默认实现在self.extensions_map表中查找文件的扩展名,默认使用application/octet流;
        """
        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['']

    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',
    })

文件路径处理

将/分隔的PATH转换为本地文件名语法。
对本地文件系统有特殊意义的组件(例如驱动器或目录名),那么可能会被阻止或诊断。

def translate_path(path):
    # abandon query parameters
    path = path.split('?', 1)[0]
    path = path.split('#', 1)[0]
    path = posixpath.normpath(unquote(path))
    words = path.split('/')
    words = filter(None, words)
    # 获取你的py文件存放的路径
    path = os.getcwd()
    # 可在此自定义路径(如果有其路径)
    path = path+"/file_xxx/xxx"
    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

信息提醒

如果HTTP Server被关闭

def signal_handler(signal, frame):
    print("You choose to stop me.")
    exit()

HTTP Server 初始化

设置HTTP Server初始数值,基于自己电脑设置。
对于IP来说,双方ip设置应一样。

# 版本设置 自定义
__version__ = "0.3.1"
def _argparse():
    parser = argparse.ArgumentParser()
    # 一般用户电脑用做服务器,每次开机后,ip4地址可能会发生变化
    ip = input("请输入IP地址:")
    parser.add_argument('--bind', '-b', metavar='ADDRESS', default=ip,
                        help='Specify alternate bind address [default: all interfaces]')
    parser.add_argument('--version', '-v', action='version', version=__version__)
    parser.add_argument('port', action='store', default=8000, type=int, nargs='?',
                        help='Specify alternate port [default: 8000]')
    return parser.parse_args()

启用HTTP Server

py程序启动后,会输出网址,点击后,会自动进入HTTP服务,可以进行文件传输操作。

def main():
    args = _argparse()
    # print(args)
    server_address = (args.bind, args.port)
    signal.signal(signal.SIGINT, signal_handler)
    signal.signal(signal.SIGTERM, signal_handler)
    httpd = HTTPServer(server_address, SimpleHTTPRequestHandler)
    server = httpd.socket.getsockname()
    print(
        "server_version: " + SimpleHTTPRequestHandler.server_version + ", python_version: " + SimpleHTTPRequestHandler.sys_version)
    print("sys encoding: " + sys.getdefaultencoding())
    print("Serving http on: " + str(server[0]) + ", port: " + str(server[1]) + " ... (http://" + server[0] + ":" + str(
        server[1]) + "/)")
    httpd.serve_forever()


if __name__ == '__main__':
    main()
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

python HTTP Server 文件上传与下载 的相关文章

随机推荐

  • 怎么搭建微服务架构?

    我要何时使用微服务架构 又如何将应用程序分解为微服务 分解后 要如何去搭建微服务架构 同时 在微服务架构中 因为会涉及到多个组件 那么这些组件又可以使用什么技术来实现呢 接下来的几个小节中 我们将对这些问题进行详细的讲解 微服务的拆分 对于
  • Jenkins

    Jenkins jenkins定义 jenkins功能 tomcat项目部署 jenkins安装 安装后的jenkins的配置 Jenkins jenkins定义 jenkins tomcat 部署 安装 jenkins 是一个开源软件项目
  • 栈的概念和基本使用

    栈 Stack 是一种线性序列结构 其操作仅限于逻辑上特定的一端 即新元素只能从栈的一端插入 也只能从栈的一端弹出 允许操作的一端叫做栈顶 禁止操作的一端叫做栈底 插入元素称为入栈 弹出元素称为出栈 栈中各个元素的操作次序遵循所谓的后进先出
  • 涅普2021训练营-MIsc(部分)

    bin txt 打开附件发现是一大串的二进制字符串 使用python直接转换成16进制 最好别用网页转换 字符串太长了 也不需要创建什么函数 直接进制转换 参考格式 hex int 字符串 2 将得到的16进制吗复制到文本 将0x删除后复制
  • git 服务端钩子做代码检查

    需求分析 在代码修改后可以对代码进行检查 比如代码规范检查 代码构建 单元测试等 我们需要禁止成员推送不符合规范的代码到服务端 Git 钩子能在特定的重要动作发生时触发自定义脚本 钩子分为客户端和服务器端两类 使用客服端钩子可以在commi
  • 字符游戏-智能蛇

    字符游戏 智能蛇 一 VT 100 终端标准 这里按照老师的课件要求 体验一下VT 100 输入输出功能以及清屏操作 代码直接复制课件中代码 这里就不再放一次了 直接给出运行效果 gcc sin demo c osin out lm sin
  • OpenAI开发系列(一):一文搞懂大模型、GPT、ChatGPT等AI概念

    全文共5000余字 预计阅读时间约10 20分钟 满满干货 建议收藏 本文目标 详细解释大型语言模型 LLM 和OpenAI的GPT系列的基本概念 一 什么是大模型 大型语言模型 也称大语言模型 大模型 Large Language Mod
  • 解决Centos7没有ens33

    进入centos7操作 ifconfig ens33 up systemctl stop NetworkManager systemctl disable NetworkManager ifup ens33 systemctl restar
  • explain查看索引使用

    CREATE TABLE test id int 11 NOT NULL name varchar 20 DEFAULT NULL dep id int 11 DEFAULT NULL age int 11 DEFAULT NULL tt
  • Qt中正确引用外部头文件和库文件的方法和注意点

    Qt中正确引入外部库文件的方法和注意点 一 什么报错是外部库导入错误导致的 二 解决外部库使用的方法 一 写入系统环境变量中的外部库调用 1 解释说明 2 使用演示 1 头文件 2 库文件 二 未写入系统环境变量中的外部库调用 1 解释说明
  • controller层

    前言 controller层代码主要流程都是 1 获取前端数据 运用request getParameter 数据名 2 创建user对象 用来传递参数 创建Service对象 用来使用Service服务的方法 3 调用Service的方法
  • C++11内存对齐之std::aligned_storage与alignas与alignof

    1 std aligned storage 插播一下POD的含义 Plain old data structure 缩写为POD 是C 语言的标准中定义的一类数据结构 POD适用于需要明确的数据底层操作的系统中 POD通常被用在系统的边界处
  • DateTime转换为时间戳

  • 记一次线性插值方法(Mathf.Lerp())的使用体会

    对Mathf Lerp 方法使用体会源于一次开发游戏对警报灯闪烁问题进行处理时 public static float Lerp float from float to float t 分析一下对线性插值函数的认识 就是在from与to之间
  • 看完这篇文章保你面试稳操胜券——小程序篇

    进大厂收藏这一系列就够了 全方位搜集总结 为大家归纳出这篇面试宝典 面试途中祝你一臂之力 共分为四个系列 本 篇 为 看 完 这 篇 文 章 保 你 面 试 稳 操 胜 券 第 四 篇
  • springboot mysql链接语句字段分析

    jdbc mysql localhost 3306 xxxx useUnicode true characterEncoding utf8 zeroDateTimeBehavior convertToNull useSSL true ser
  • 几个简单的system(const char* _Command)函数命令

    几个简单的system const char Command 函数命令 呼出终端 Windows键 r 然后输入cmd system const char Command 函数常用命令 如 system cls 1 shutdown常用命令
  • JS 实现全屏切换,移动端适用

    JS 实现全屏切换 移动端适用 直接看代码吧 简单 只是有些人不知道这个 api 我之前就不知道
  • tensorflow搭建自己的残差网络(ResNet)

    废话不说 直接上代码 首先 pip install tflearn 训练代码 coding utf 8 from future import division print function absolute import import tf
  • python HTTP Server 文件上传与下载

    实现在局域网 同一WIFI下 文件上传与下载 该模块通过实现标准GET在BaseHTTPServer上构建 和HEAD请求 将所有代码粘贴到同一个py文件中 即可使用 所需包 基于python3版本实现 python2版本无涉猎 impor