python3中http协议提供文件服务器功能

http协议是互联网的通用基础协议,也可以利用其来开发文件服务器,给客户提供文件浏览,查看,下载,上传等功能。

目录

1.python3自带http文件服务

 2.python3从头开发http文件服务


1.python3自带http文件服务

python3中http.server提供http文件服务,默认端口是8000,可以进行修改

运行命令:

python3 -m http.server 12567

另外python2中可以使用SimpleHTTPServer来提供http文件服务,默认端口是8000,ye可以进行修改

运行命令:

python -m SimpleHTTPServer 12567
运行效果如下:

 

 2.python3从头开发http文件服务

http_server.py

# -*- coding: UTF-8 -*-

import os, time
import sys, socket
import posixpath
#escape:逃跑,用来让特殊符号表示它本来的意思
try:
    from html import escape
except ImportError:
    from cgi import escape
import shutil
import mimetypes
import re
import signal
from io import StringIO, BytesIO
import codecs
from urllib.parse import quote
from urllib.parse import unquote
import urllib.parse
from http.server import HTTPServer
from http.server import BaseHTTPRequestHandler

"""urlencode quote unquote
urllib.parse.urlencode
urlencode函数,可以把key-value这样的键值对转换成我们想要的格式,返回的是a=1&b=2这样的字符串
urllib.parse.quote
quote对一个字符串进行urlencode转换
urllib.parse.unquote
unquote与quote对应,用来解码quote处理后的结果
"""

"""内存IO之StringIO和BytesIO
参考博客:
https://zhuanlan.zhihu.com/p/332651899
"""

"""HTTPServer
参考博客
    https://www.cnblogs.com/jason-huawen/p/16241405.html
"""
"""
浏览器运行:
    http://127.0.0.1:18081
"""

class MyHTTPRequestHandler(BaseHTTPRequestHandler):
    address = socket.gethostbyname(socket.gethostname())
    treefile = "sesssion_cache.txt"
    mylist = []
    myspace = ""
       
    def do_GET(self):
        """处理GET请求
        """
        print(MyHTTPRequestHandler.address)
        paths = unquote(self.path)
        path = str(paths)
        print("path is ", paths, path)
        '''
        self.send_error(404, "File not found")
        '''
        if self.remove_dir_or_file(path):
            return
        fd = self.send_head()
        if fd:
            #关键代码: self.wfile用来向客户端写入数据
            shutil.copyfileobj(fd, self.wfile)
            if path == "/":
                self.traversal_file(translate_path(self.path))
                self.write_list(MyHTTPRequestHandler.treefile)
            fd.close()
            
    def do_HEAD(self):
        """处理HEAD请求
        """
        fd = self.send_head()
        if fd:
            fd.close()
    
    def do_POST(self):
        """处理POST请求
        """
        r, info = self.deal_post_data()
        #拼装HTML文本
        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><br>")
        else:
            f.write(b"<strong>Failed:</strong><br>")

        for i in info:
            print(r, i, "by: ", self.client_address)
            f.write(i.encode('utf-8')+b"<br>")
        f.write(b"<br><a href=\"%s\">back</a>" % self.headers['referer'].encode('ascii'))
        f.write(b"</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()
        #每次提交post请求之后更新目录树文件
        self.p(translate_path(self.path))
        self.write_list(MyHTTPRequestHandler.treefile)
    
    def str_to_chinese(self,var):
        not_end = True
        while not_end:
            start1 = var.find("\\x")
            # print start1
            if start1 > -1:
                str1 = var[start1 + 2:start1 + 4]
                print(str1)
                start2 = var[start1 + 4:].find("\\x") + start1 + 4
                if start2 > -1:
                    str2 = var[start2 + 2:start2 + 4]

                    start3 = var[start2 + 4:].find("\\x") + start2 + 4
                    if start3 > -1:
                        str3 = var[start3 + 2:start3 + 4]
            else:
                not_end = False
            if start1 > -1 and start2 > -1 and start3 > -1:
                str_all = str1 + str2 + str3
                # print str_all
                str_all = codecs.decode(str_all, "hex").decode('utf-8')

                str_re = var[start1:start3 + 4]
                # print str_all, "   " ,str_re
                var = var.replace(str_re, str_all)
        # print var.decode('utf-8')
        return var
    
    def deal_post_data(self):
        boundary = self.headers["Content-Type"].split("=")[1].encode('ascii')
        print("boundary===", boundary)
        remain_bytes = int(self.headers['content-length'])
        print("remain_bytes===", remain_bytes)

        res = []
        line = self.rfile.readline()
        while boundary in line and str(line, encoding = "utf-8")[-4:] != "--\r\n":
            
            #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)
            print("line!!!",line)
            fn = re.findall(r'Content-Disposition.*name="file"; filename="(.*)"', str(line))
            if not fn:
                return False, "Can't find out file name..."
            path = translate_path(self.path)

            fname = fn[0]
            #fname = fname.replace("\\", "\\\\")
            fname = self.str_to_chinese(fname)
            print("------",fname)
            
            fn = os.path.join(path, fname)
            while os.path.exists(fn):
                fn += "_"
            print("!!!!",fn)
            dirname = os.path.dirname(fn)
            if not os.path.exists(dirname):
                os.makedirs(dirname)
            line = self.rfile.readline()
            remain_bytes -= len(line)
            line = self.rfile.readline()
            # b'\r\n'
            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()
            print("pre_line", pre_line)
            remain_bytes -= len(pre_line)
            print("remain_bytes", remain_bytes)
            Flag = True
            while remain_bytes > 0:
                line = self.rfile.readline()
                print("while line", line)
                
                if boundary in line:
                    remain_bytes -= len(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()
                    res.append("File '%s' upload success!" % fn)
                    Flag = False
                    break
                else:
                    out.write(pre_line)
                    pre_line = line
            if pre_line is not None and Flag == True:
                out.write(pre_line)
                out.close()
                res.append("File '%s' upload success!" % fn)
        return True, res
    
    def remove_dir_or_file(self, path):
        plist = path.split("/", 2)
        print("plist plist plist plist ", plist)
        if len(plist) > 2 and plist[1] == "delete":
            #路径转化
            file = translate_path(plist[2])
            print("======>>>>>>>>>> file", file)
            if os.path.exists(file):
                fdir = os.path.dirname(file)
                print("======>>>>>>>>>> ", file, fdir)
                #删除文件
                os.remove(file)
                if not os.listdir(fdir):
                    #删除目录
                    os.removedirs(fdir)
                time.sleep(0.5)
                # 0.5s后重定向
                self.send_response(302)
                self.send_header('Location', "/")
                self.end_headers()
                return True
            print("======>>>>>>>>>> file not exists ", file)
        return False
            
    def send_head(self):
        """发送HTTP头
        """
        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)
        print("=================================")
        content_type = self.guess_type(path)
        try:
            # Always read in binary mode. Opening files in text mode may cause
            # newline translations, making the actual size of the content
            # transmitted *less* than the content-length!
            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):
        """Helper to produce a directory listing (absent index.html).
        Return value is either a file object, or None (indicating an
        error).  In either case, the headers are sent, making the
        interface the same as for 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('ascii'))
        f.write(b"<body>\n<h2>Directory listing for %s</h2>\n" % display_path.encode('ascii'))
        f.write(b"<hr>\n")
        #上传目录
        f.write(b"<h3>Directory Updating</h3>\n")
        f.write(b"<form ENCTYPE=\"multipart/form-data\" method=\"post\">")
        #@change=\"handleChange\" @click=\"handelClick\"
        f.write(b"<input ref=\"input\" webkitdirectory multiple name=\"file\" type=\"file\"/>")
        f.write(b"<input type=\"submit\" value=\"uploadDir\"/></form>\n")
        f.write(b"<hr>\n")
        #上传文件
        f.write(b"<h3>Files Updating</h3>\n")
        f.write(b"<form ENCTYPE=\"multipart/form-data\" method=\"post\">")
        f.write(b"<input ref=\"input\" multiple name=\"file\" type=\"file\"/>")
        f.write(b"<input type=\"submit\" value=\"uploadFiles\"/></form>\n")

        f.write(b"<hr>\n")
        #表格
        f.write(b"<table with=\"100%\">")
        f.write(b"<tr><th>path</th>")
        f.write(b"<th>size(Byte)</th>")
        f.write(b"<th>modify time</th>")
        f.write(b"</tr>")

        # 根目录下所有的内容
        for name in list_dir:
            # 根目录下的路径
            fullname = os.path.join(path, name)
            # 目录名/文件名
            display_name = linkname = name
            print("display_name ==> ", display_name)
            if display_name.upper() == "HTTP_SERVER.PY":
                continue

            # 如果是文件夹的话
            if os.path.isdir(fullname):
                # 遍历文件夹
                for root, dirs, files in os.walk(fullname):
                # root 表示当前正在访问的文件夹路径
                # dirs 表示该文件夹下的子目录名list
                # files 表示该文件夹下的文件list
                    # 遍历文件
                    for fi in files:
                        print("########", os.path.join(root, fi))
                        display_name = os.path.join(root, fi)
                        #删除前面的xx个字符,取出相对路径
                        relativePath = display_name[len(os.getcwd()):].replace('\\','/')
                        st = os.stat(display_name)
                        fsize = st.st_size
                        fmtime = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(st.st_mtime))
                        f.write(b"<tr>")
                        f.write(b'<td><a href="%s">%s</a></td>' % (quote(relativePath).encode('utf-8'), escape(relativePath).encode('utf-8')))
                        f.write(b"<td>%d</td>" % fsize)
                        f.write(b"<td>%s</td>" % escape(fmtime).encode('ascii'))
                        f.write(b"<td><a href=\"/delete/%s\">delete</a>" % escape(relativePath).encode('utf-8'))
                        f.write(b"</tr>")
                
                    # 遍历所有的文件夹名字,其实在上面walk已经遍历过了
                    # for d in dirs:
                    #     print(d)
                        
            # 如果是链接文件
            elif os.path.islink(fullname):
                linkname = linkname + "/"
                print("real===", linkname)
                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('ascii'), escape(display_name).encode('ascii')))
            
            else:
                #其他直接在根目录下的文件直接显示出来
                st = os.stat(display_name)
                fsize = st.st_size
                fmtime = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(st.st_mtime))
                f.write(b"<tr>")
                f.write(b'<td><a href="%s">%s</a></td>' % (quote(linkname).encode('utf-8'), escape(display_name).encode('utf-8')))
                f.write(b"<td>%d</td>" % fsize)
                f.write(b"<td>%s</td>" % escape(fmtime).encode('ascii'))
                f.write(b"<td><a href=\"/delete/%s\">delete</a>" % escape(display_name).encode('utf-8'))
                f.write(b"</tr>")

        f.write(b"</table>")
        f.write(b"\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):
        """Guess the type of a file.
        Argument is a PATH (a filename).
        Return value is a string of the form type/subtype,
        usable for a MIME Content-type header.
        The default implementation looks the file's extension
        up in the table self.extensions_map, using application/octet-stream
        as a default; however it would be permissible (if
        slow) to look inside the data to make a better guess.
        """

        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',
        })
        
    def traversal_file(self, url):
        print("url:", url)
        files = os.listdir(r''+url)
        for file in files:           
            myfile = url + "//" + file
            size = os.path.getsize(myfile)
            if os.path.isfile(myfile):
                MyHTTPRequestHandler.mylist.append(str(MyHTTPRequestHandler.myspace)+"|____"+file +" "+ str(size)+"\n")
            
            if os.path.isdir(myfile) :
                MyHTTPRequestHandler.mylist.append(str(MyHTTPRequestHandler.myspace)+"|____"+file + "\n")
                #get into the sub-directory,add "|    "
                MyHTTPRequestHandler.myspace = MyHTTPRequestHandler.myspace+"|    "
                self.traversal_file(myfile)
                #when sub-directory of iteration is finished,reduce "|    "
                MyHTTPRequestHandler.myspace = MyHTTPRequestHandler.myspace[:-5]
                
    def get_all_file_list(self):
        listofme = []
        for root, dirs, files in os.walk(translate_path(self.path)):
            files.sort()
            for fi in files:
                display_name = os.path.join(root, fi)
                #删除前面的XXX个字符,取出相对当前目录的路径
                relativePath = display_name[len(os.getcwd()):].replace('\\','/')
                st = os.stat(display_name)
                fsize = st.st_size
                fmtime = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(st.st_mtime))
                listofme.append(relativePath+"\t")
                listofme.append(str(fsize)+"\t")
                listofme.append(str(fmtime)+"\t\n")
        return listofme
    
    def write_list(self,url):
        f = open(url,'w')
        f.write("http://"+str(MyHTTPRequestHandler.address)+":8001/ directory tree\n")
        MyHTTPRequestHandler.mylist.sort()
        f.writelines(MyHTTPRequestHandler.mylist)
        f.write("\nFile Path\tFile Size\tFile Modify Time\n")
        f.writelines(self.get_all_file_list())
        MyHTTPRequestHandler.mylist = []
        MyHTTPRequestHandler.myspace = ""
        f.close()
        print("write_list end")

def translate_path(path):
    path = path.split('?', 1)[0]
    path = path.split('#', 1)[0]
    path = posixpath.normpath(unquote(path))
    words = path.split('/')
    words = filter(None, words)
    path = os.getcwd()
    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
       
def signal_handler(signal, frame):
    print(signal, frame)
    exit()


def main():
    print('python version:', sys.version_info.major, sys.version_info.minor)
    if sys.argv[1:]:
        port = int(sys.argv[1])
    else:
        port = 18081
    #server_address = ('', port)
    address = ('127.0.0.1', port)

    signal.signal(signal.SIGINT, signal_handler)
    signal.signal(signal.SIGTERM, signal_handler)
    signal.signal(signal.SIGQUIT, signal_handler)
    #忽略ctrl+z:SIGTSTP(挂起信号)
    signal.signal(signal.SIGTSTP, signal.SIG_IGN)

    server = HTTPServer(address, MyHTTPRequestHandler)
    server_info = server.socket.getsockname()
    print("server info: " + str(server_info[0]) + ", port: " + str(server_info[1]) + " ...")
    server.serve_forever()

def test_url_code():
    values = {'username': 'hello 你好', 'password': 'pass'}
    data = urllib.parse.urlencode(values)
    print("data = ", data)
    
    name = '狄仁杰'
    name = urllib.parse.quote(name)
    print("name = ", name)

    dname = urllib.parse.unquote(name)
    print("dname = " + dname)
    
if __name__ == '__main__':
    test_url_code()
    main()

运行效果如下:

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要使用Python3文件从一个服务器传输到另一个服务器,可以使用paramiko模块实现。这个模块允许你使用SSH协议连接到远程服务器并执行文件传输操作。 以下是实现文件传输的简单示例代码: ``` import paramiko # 设置源和目标服务器的主机名、用户名和密码 src_hostname = 'source_server' src_username = 'source_username' src_password = 'source_password' dst_hostname = 'destination_server' dst_username = 'destination_username' dst_password = 'destination_password' # 创建SSH客户端连接到源服务器 src_ssh = paramiko.SSHClient() src_ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) src_ssh.connect(src_hostname, username=src_username, password=src_password) # 创建SSH客户端连接到目标服务器 dst_ssh = paramiko.SSHClient() dst_ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) dst_ssh.connect(dst_hostname, username=dst_username, password=dst_password) # 创建SFTP客户端连接到源服务器 src_sftp = src_ssh.open_sftp() # 在源服务器上打开要传输的文件 local_path = '/path/to/local/file' remote_path = '/path/to/remote/file' src_file = src_sftp.open(local_path, 'rb') # 在目标服务器上打开要传输的文件 dst_sftp = dst_ssh.open_sftp() dst_file = dst_sftp.open(remote_path, 'wb') # 从源服务器读取数据并写入目标服务器 dst_file.write(src_file.read()) # 关闭文件和SFTP连接 src_file.close() dst_file.close() src_sftp.close() dst_sftp.close() # 关闭SSH连接 src_ssh.close() dst_ssh.close() ``` 这个示例代码使用paramiko模块连接到源服务器和目标服务器,并使用SFTP协议进行文件传输。在示例代码,你需要将`src_hostname`、`src_username`、`src_password`、`dst_hostname`、`dst_username`和`dst_password`设置为源服务器和目标服务器的实际信息,将`local_path`和`remote_path`设置为要传输的文件的本地路径和远程路径。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值