python HTTP Server 文件上传与下载

发布时间 2023-12-01 11:09:40作者: jack_Meng

python HTTP Server 文件上传与下载

实现在局域网(同一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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

基本类 简单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',
    })

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197

文件路径处理

将/分隔的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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

信息提醒

如果HTTP Server被关闭

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

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()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

启用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()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

 

【出处】:https://blog.csdn.net/love_wgll/article/details/129155712

=======================================================================================

python上传下载文件

场景:

  • 点击上传文件按钮,选择需要上传的文件后上传
  • 文件上传成功后,会将文件保存到指定目录下
  • 限制上传文件的格式
  • 在前端点击文件后下载
  • 基于上述上传并保存到指定目录下的文件

上手

文件上传

app.py

@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
    # 渲染文件
    return render_template('upload.html')

# 文件保存的目录,根据实际情况的文件结构做调整;
# 若不指定目录,可以写成f.save(f.filename),可以默认保存到当前文件夹下的根目录
# 设置上传文件保存路径 可以是指定绝对路径,也可以是相对路径(测试过)
app.config['UPLOAD_FOLDER'] = './upload'
# 将地址赋值给变量
file_dir = app.config['UPLOAD_FOLDER']


@app.route('/uploader', methods=['GET', 'POST'])
def uploader():
    """
        文件上传
    """
    if request.method == 'POST':
        # input标签中的name的属性值
        f = request.files['file']

        # 拼接地址,上传地址,f.filename:直接获取文件名
        f.save(os.path.join(app.config['UPLOAD_FOLDER'], f.filename))
        # 输出上传的文件名
        print(request.files, f.filename)

        return '文件上传成功!'
    else:
        return render_template('upload.html')
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
 
 

upload.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>上传</title>

        <style>
            a{
                text-decoration: none;
                color: #2062e0;
            }
            a:hover{
                text-decoration: none;
                color: #2062e0;
            }
        </style>
    </head>
    <body>
        <h1 align="center">TEST</h1>
        <div align="center">
        	{# enctype:属性用于指定将上传文件数据发送给服务器端之前对表单数据进行编码的斱式 #}
        	{# enctype="multipart/form-data" => 表示不对字符编码。当使用有文件上传控件的表单时,该值是必需的。 #}
            <form action="/uploader" method="post" enctype="multipart/form-data">
                <br><br><br>
                {# accept可以自定以文件上传格式 #}
                <input type="file" name="file" accept=".txt, .pdf, .doc, .docx, .md" value="{{ csrf_token }}" />
                <br><br><br>
                <input type="submit" value="提交" />
            </form>

            <br><br><br>
        </div>
    </body>
</html>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
 
 

文件下载

app.py

@app.route('/download', methods=['GET', 'POST'])
def download():
    """
        文件下载
    :return:
    """
    timelist = []   # 获取指定文件夹下文件并显示
    Foder_Name = []     # 文件夹下所有文件
    Files_Name = []     # 文件名

    # 获取到指定文件夹下所有文件
    lists = os.listdir(file_dir + '/')

    # 遍历文件夹下所有文件
    for i in lists:
        # os.path.getatime => 获取对指定路径的最后访问时间
        timelist.append(time.ctime(os.path.getatime(file_dir + '/' + i)))

    # 遍历文件夹下的所有文件
    for k in range(len(lists)):
        # 单显示文件名
        Files_Name.append(lists[k])
        # 获取文件名以及时间信息
        Foder_Name.append(lists[k] + " ~~~~~~~~~~~~~~~~~~~~~ " + timelist[k])

    print(file_dir)     # ./upload

    return render_template('download.html', allname=Foder_Name, name=Files_Name)


@app.route('/downloads/<path:path>', methods=['GET', 'POST'])
def downloads(path):
    """
        重写download方法,根据前端点击的文件传送过来的path,下载文件
	
		send_from_directory:用于下载文件
		flask.send_from_directory(所有文件的存储目录,相对于要下载的目录的文件名,as_attachment:设置为True是否要发送带有标题的文件)
    :param path:
    :return:
    """
    return send_from_directory(app.config['UPLOAD_FOLDER'], path, as_attachment=True)
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
 
 

download.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>文件下载</title>

        <style>
            a{
                text-decoration: none;
                color: #2062e0;
            }
            a:hover{
                text-decoration: none;
                color: #2062e0;
            }
        </style>

    </head>
    <body>
        <div align="center">
            <h1>文件下载</h1><br><br>
            {# 输出文件名及文件详细信息(文件时间信息等) #}
            {% for fruit in allname %}
                <br>
                {{ fruit }}
            {% endfor %}
            <br><br><br><br>
            {# 将指定文件夹中的文件获取遍历显示 #}
            {% for n in name %}

                <a href="downloads/{{ n }}">{{ n }}</a>
                <br><br>

            {% endfor %}
        </div>
    </body>
</html>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
 
 

运行

  • 文件上传和下载的视图函数代码都完成后,开始运行项目

app.py(在编写好试图代码的最下方编写运行代码)

"""
    运行项目
"""
if __name__ == '__main__':
	# 可以使统一局域网下的其他电脑访问该项目
    HOST = '0.0.0.0'
    # debug=True => 打开调试模式,在对代码进行修改后,可以实时运行代码
    app.run(host=HOST, debug=True)
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
 
 
  • 打开调试模式后,运行项目建议在Pycharm中的Terminal命令行中输入以下运行
python app.py
  • 1.
 
 

整个app.py代码

import os
import time

from flask import Flask, render_template, request, send_from_directory

app = Flask(__name__)


@app.route('/')
def hello_world():
    return 'Hello World!'


@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
    # 渲染文件
    return render_template('upload.html')


# 文件保存的目录,根据实际情况的文件结构做调整;
# 若不指定目录,可以写成f.save(f.filename),可以默认保存到当前文件夹下的根目录
# 设置上传文件保存路径 可以是指定绝对路径,也可以是相对路径(测试过)
app.config['UPLOAD_FOLDER'] = './upload'	## 该目录需要自行创建
# 将地址赋值给变量
file_dir = app.config['UPLOAD_FOLDER']


@app.route('/uploader', methods=['GET', 'POST'])
def uploader():
    """  文件上传  """
    if request.method == 'POST':
        # input标签中的name的属性值
        f = request.files['file']

        # 拼接地址,上传地址,f.filename:直接获取文件名
        f.save(os.path.join(app.config['UPLOAD_FOLDER'], f.filename))
        # 输出上传的文件名
        print(request.files, f.filename)

        return '文件上传成功!'
    else:
        return render_template('upload.html')


@app.route('/download', methods=['GET', 'POST'])
def download():
    """  文件下载  """
    timelist = []  # 获取指定文件夹下文件并显示
    Foder_Name = []  # 文件夹下所有文件
    Files_Name = []  # 文件名

    # 获取到指定文件夹下所有文件
    lists = os.listdir(file_dir + '/')

    # 遍历文件夹下所有文件
    for i in lists:
        # os.path.getatime => 获取对指定路径的最后访问时间
        timelist.append(time.ctime(os.path.getatime(file_dir + '/' + i)))

    # 遍历文件夹下的所有文件
    for k in range(len(lists)):
        # 单显示文件名
        Files_Name.append(lists[k])
        # 获取文件名以及时间信息
        Foder_Name.append(lists[k] + " ~~~~~~~~~~~~~~~~~~~~~ " + timelist[k])

    print(file_dir)  # ./upload

    return render_template('download.html', allname=Foder_Name, name=Files_Name)


@app.route('/downloads/<path:path>', methods=['GET', 'POST'])
def downloads(path):
    """ 下载 """
    """
        重写download方法,根据前端点击的文件传送过来的path,下载文件
		send_from_directory:用于下载文件
		flask.send_from_directory(所有文件的存储目录,相对于要下载的目录的文件名,as_attachment:设置为True是否要发送带有标题的文件)
    """
    return send_from_directory(app.config['UPLOAD_FOLDER'], path, as_attachment=True)


"""
    运行项目
"""
if __name__ == '__main__':
    # 可以使统一局域网下的其他电脑访问该项目
    HOST = '0.0.0.0'
    # debug=True => 打开调试模式,在对代码进行修改后,可以实时运行代码
    app.run(host=HOST, debug=True)
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
 
 

模板文件中代码已完整

注意

  • 代码编写好后,需要在项目根目录先创建一个路径用于存放上传的文件
  • 即创建upload文件夹
  • 运行项目后,需要在浏览器地址后补充输入/upload,即可进入上传页面
  • http://127.0.0.1:5000/upload,具体端口号需根据个人项目运行修改,可以在运行时看控制台

 

模拟文件上传Python python上传下载文件_flask

 

 

模拟文件上传Python python上传下载文件_python_02

 

 

 

  • 下载页面

 

模拟文件上传Python python上传下载文件_python_03

 

项目文件目录

模拟文件上传Python python上传下载文件_python_04

 

 

出处:https://blog.51cto.com/u_14273/7758108

=======================================================================================

python-上传下载文件

一、服务端接口

复制代码
import flask, os,sys,time
from flask import request, send_from_directory

interface_path = os.path.dirname(__file__)
sys.path.insert(0, interface_path)  #将当前文件的父目录加入临时系统变量


server = flask.Flask(__name__)


#get方法:指定目录下载文件
@server.route('/download', methods=['get'])
def download():
    fpath = request.values.get('path', '') #获取文件路径
    fname = request.values.get('filename', '')  #获取文件名
    if fname.strip() and fpath.strip():
        print(fname, fpath)
        if os.path.isfile(os.path.join(fpath,fname)) and os.path.isdir(fpath):
            return send_from_directory(fpath, fname, as_attachment=True) #返回要下载的文件内容给客户端
        else:
            return '{"msg":"参数不正确"}'
    else:
        return '{"msg":"请输入参数"}'


# get方法:查询当前路径下的所有文件
@server.route('/getfiles', methods=['get'])
def getfiles():
    fpath = request.values.get('fpath', '') #获取用户输入的目录
    print(fpath)
    if os.path.isdir(fpath):
        filelist = os.listdir(fpath)
        files = [file for file in filelist if os.path.isfile(os.path.join(fpath, file))]
    return '{"files":"%s"}' % files


# post方法:上传文件的
@server.route('/upload', methods=['post'])
def upload():
    fname = request.files.get('file')  #获取上传的文件
    if fname:
        t = time.strftime('%Y%m%d%H%M%S')
        new_fname = r'upload/' + t + fname.filename
        fname.save(new_fname)  #保存文件到指定路径
        return '{"code": "ok"}'
    else:
        return '{"msg": "请上传文件!"}'


server.run(port=8000, debug=True)
复制代码

 

二、客户端发送请求

复制代码
import requests
import os


#上传文件到服务器
file = {'file': open('hello.txt','rb')}
r = requests.post('http://127.0.0.1:8000/upload', files=file)
print(r.text)


#查询fpath下的所有文件
r1 = requests.get('http://127.0.0.1:8000/getfiles',data={'fpath': r'download/'})
print(r1.text)


#下载服务器download目录下的指定文件
r2 = requests.get('http://127.0.0.1:8000/download',data={'filename':'hello_upload.txt', 'path': r'upload/'})
file = r2.text #获取文件内容
basepath = os.path.join(os.path.dirname(__file__), r'download/')
with open(os.path.join(basepath, 'hello_download.txt'),'w',encoding='utf-8') as f: #保存文件
    f.write(file)
复制代码

 

 

【出处】:https://www.cnblogs.com/jessicaxu/p/7891372.html

=======================================================================================

python-文件上传下载,解决粘包问题

一、数据粘包

【1】客户端两次发送请求,但是可能被服务端的同个recv收到,不能区分,会造成数据粘包(实际上需要服务端将两次请求区分接受)

二、服务器

# -*- coding:utf-8 -*-
# __author__:pansy
# 2022/5/14
import socket

# 创建socket对象
sk = socket.socket()
# 给服务器绑定ip和端口
sk.bind(('127.0.0.1',8889))
# 创建监听,监听客户端是否有发请求过来
sk.listen()

def get_file(sk_obj):
    '''
    接收文件
    :param sk_obj: 文件对象
    :return:
    '''
    # 从服务端会发送1个请求,用来传输文件大小,文件大小是整形,需要将string类型强转成int类型
    file_size = int(sk_obj.recv(1024).decode('utf8'))
    # 为了避免粘包,当执行完接收file_size语句后,需要告知post_file,文件大小已经接收成功
    sk_obj.sendall(b'ok')

    # 从服务端会发送1个请求,用来传输文件名称
    file_name = sk_obj.recv(1024).decode('utf8')
    sk_obj.sendall(b'ok')

    # 接收文件内容
    with open('./%s' %file_name,'wb') as f:
        while file_size > 0:
            f.write(sk_obj.recv(1024))
            file_size -= 1024


# 阻塞状态,若接收到数据,则阻塞解除
# accept返回一个套接字和客户端的ip端口
conn ,addr = sk.accept()

# 调用接收文件方法,conn是专门用来处理客户端业务的套接字
get_file(conn)

conn.close()
sk.close()

三、客户端

# -*- coding:utf-8 -*-
# __author__:pansy
# 2022/5/14
import os
import socket

# 创建socket对象
sk = socket.socket()
# 连接服务器,连接的是服务器绑定的ip和端口
sk.connect(('127.0.0.1',8889))

def post_file(sk_obj,file_path):
    '''
    发送文件,需要和接收文件一一对应
    :param sk_obj:文件对象
    :param file_path:文件路径
    :return:
    '''

    # 发送文件大小,用os.stat方法可以获取文件属性
    file_size = os.stat(file_path).st_size
    # 获取到的file_size是整形,不能直接编码,所以需要先强转成字符串
    sk_obj.sendall(str(file_size).encode('utf8'))
    # 为了避免粘包,需要用recv接收下参数,直到接收到ok后,才会继续下面的代码
    sk_obj.recv(1024)

    # 发送文件名称,用os.path.split方法,可以将文件路径切割成路径和文件名,返回这两个字段
    file_name = os.path.split(file_path)[1]
    sk_obj.sendall(file_name.encode('utf8'))
    sk_obj.recv(1024)

    # 发送文件内容,循环发送,1次发送1024个字节
    # 1、先读取文件,用rb二进制读取
    with open(file_path,'rb') as f:
        # 每发送1次,file_size会减少1024,不满足1024的,全部发送
        while file_size > 0:
            sk_obj.sendall(f.read(1024))
            file_size -= 1024

# 调用发送文件方法
path = '/Users/panshaoying/Desktop/database/data/img1.png'
post_file(sk,path)

sk.close()

 

【出处】:https://www.cnblogs.com/flowers-pansy/p/16269797.html

=======================================================================================