• Flask 学习55.文件上传功能开发 上海


    前言

    文件上传的基本原理实际上很简单,基本上是:

    • 一个带有 enctype=multipart/form-data <form> 标记,标记中含有 一个 <input type=file>
    • 应用通过 request 对象的 files 字典来访问文件。
    • 使用文件的 save() 方法把文件 永久地保存在文件系统中。

    简单介绍

    从最基本的功能开始,这个应用上传文件到一个指定目录,并把文件显示给用户。
    以下是应用的部分代码:

    import os
    from flask import Flask, flash, request, redirect, url_for
    from werkzeug.utils import secure_filename
    
    UPLOAD_FOLDER = '/path/to/the/uploads'
    ALLOWED_EXTENSIONS = {'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'}
    
    app = Flask(__name__)
    app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
    

    UPLOAD_FOLDER 是上传文 件要储存的目录, ALLOWED_EXTENSIONS 是允许上传的文件扩展名的集合。

    为什么要限制文件件的扩展名呢?如果直接向客户端发送数据,那么你可能不会想让 用户上传任意文件。否则,你必须确保用户不能上传 HTML 文件,因为 HTML 可能引 起 XSS 问题(参见 跨站脚本攻击(XSS) )。如果服务器可以执行 PHP 文件,那么还必须确 保不允许上传 .php 文件。但是谁又会在服务器上安装 PHP 呢,对不?

    下一个函数secure_filename()检查扩展名是否合法,上传文件,把用户重定向到已上传文件的 URL:

    def allowed_file(filename):
        return '.' in filename and \
               filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
    
    @app.route('/', methods=['GET', 'POST'])
    def upload_file():
        if request.method == 'POST':
            # check if the post request has the file part
            if 'file' not in request.files:
                flash('No file part')
                return redirect(request.url)
            file = request.files['file']
            # if user does not select file, browser also
            # submit an empty part without filename
            if file.filename == '':
                flash('No selected file')
                return redirect(request.url)
            if file and allowed_file(file.filename):
                filename = secure_filename(file.filename)
                file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
                return redirect(url_for('uploaded_file',
                                        filename=filename))
        return '''
        <!doctype html>
        <title>Upload new File</title>
        <h1>Upload new File</h1>
        <form method=post enctype=multipart/form-data>
          <input type=file name=file>
          <input type=submit value=Upload>
        </form>
        '''
    

    secure_filename() 函数到

    那么 secure_filename() 函数到底是有什么用?
    有一条原则是“永远不要信任用户输入”。这条原则同样适用于已上传文件的文件名。
    所有提 交的表单数据可能是伪造的,文件名也可以是危险的。此时要谨记:在把文件保存到 文件系统之前总是要使用这个函数对文件名进行安检。

    你可以会好奇 secure_filename() 做了哪些工作,如果 不使用它会有什么后果。假设有人把下面的信息作为 filename 传递给你的应 用:
    filename = "../../../../home/username/.bashrc"
    
    假设 ../ 的个数是正确的,你会把它和 UPLOAD_FOLDER 结合在一起,那 么用户就可能有能力修改一个服务器上的文件,这个文件本来是用户无权修改的。 这需要了解应用是如何运行的,但是请相信我,黑客都是很变态的 :)
    
    现在来看看函数是如何工作的:
    
    >>> secure_filename('../../../../home/username/.bashrc')
    'home_username_.bashrc'
    
    
    

    现在还剩下一件事:为已上传的文件提供服务。
    在 upload_file() 中,我 们把用户重定向到 url_for('uploaded_file', filename=filename) ,即 /uploads/filename
    因此我们写一个 uploaded_file() 来返回该文件 名称。 Flask 0.5 版本开始我们可以使用一个函数来完成这个任务:

    from flask import send_from_directory
    
    @app.route('/uploads/<filename>')
    def uploaded_file(filename):
        return send_from_directory(app.config['UPLOAD_FOLDER'],
                                   filename)
    

    另外,可以把 uploaded_file 注册为 build_only 规则,并使用 SharedDataMiddleware 。这种方式可以在 Flask 老版本 中使用:

    from werkzeug import SharedDataMiddleware
    app.add_url_rule('/uploads/<filename>', 'uploaded_file',
                     build_only=True)
    app.wsgi_app = SharedDataMiddleware(app.wsgi_app, {
        '/uploads':  app.config['UPLOAD_FOLDER']
    })
    

    MAX_CONTENT_LENGTH 限制文件尺寸

    Flask 到底是如何处理文件上传的呢?如果上传的文件很小,那么会把它们储存在内 存中。否则就会把它们保存到一个临时的位置(通过 tempfile.gettempdir() 可以得到这个位置)。
    但是,如何限制上传文件的尺寸呢?缺省情况下, Flask 是 不限制上传文件的尺寸的。可以通过设置配置的 MAX_CONTENT_LENGTH 来限制文 件尺寸:

    from flask import Flask, Request
    
    app = Flask(__name__)
    app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024
    

    上面的代码会把尺寸限制为 16 M 。如果上传了大于这个尺寸的文件, Flask 会抛 出一个 RequestEntityTooLarge 异常。

    更多信息请参阅 Werkzeug 关于文件处理的文档。

    使用示例

    在模板中,form 标签添加属性 enctype="multipart/form-data"
    upfile.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>上传文件功能</title>
    </head>
    <body>
        <form method="post" enctype="multipart/form-data">
            <div>
                <label for="title">文件名称</label>
                <input id="title" type="text" name="title">
            </div>
            <div>
                <label for="file">文件名称</label>
                <input id="file" type="file" name="file">
            </div>
          <input type=submit value="提交">
        </form>
    </body>
    </html>
    

    上传文件视图

    import os
    from flask import Flask, flash, request, redirect, url_for, render_template
    from werkzeug.utils import secure_filename
    from flask import send_from_directory
    BASE_DIR = os.path.dirname(os.path.realpath(__file__))
    UPLOAD_FOLDER = os.path.join(BASE_DIR, 'media')
    ALLOWED_EXTENSIONS = {'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'}
    
    
    app = Flask(__name__)
    app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
    app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024
    
    def allowed_file(filename):
        return '.' in filename and \
               filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
    
    
    @app.route('/upfile', methods=['GET', 'POST'])
    def up_file():
        if request.method == 'GET':
            return render_template('upfile.html')
        if request.method == 'POST':
            title = request.form.get('title')  # form 获取表单参数
            file = request.files.get('file')  # file 或取文件参数
            if file and allowed_file(file.filename):
                filename = secure_filename(file.filename)  # 校验文件名称合法
                print(filename)
                file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
                return {
                    'msg': 'success',
                    'url': f'/images/{filename}/',
                }
            else:
                return {
                    'msg': '文件格式不支持'
                }
    
    
    @app.route('/images/<filename>/')
    def get_image(filename):
        return send_from_directory(UPLOAD_FOLDER, filename)
    
    
    if __name__ == '__main__':
        app.run()
    
    

    上传图片后保存到media目录

    接口返回

    访问图片地址,就可以访问图片了
    http://127.0.0.1:5000/images/abc.jpg/

    send_from_directory 获取文件

    从服务器上读取文件视图函数中使用 send_from_directory(文件的目录,文件名)来获取

    from flask import send_from_directory
    
    @app.route('/images/<filename>/')
    def get_image(filename):
        return send_from_directory(UPLOAD_PATH, filename)
    
  • 相关阅读:
    网络编程
    Python之异常处理
    python第31天作业(面向对象高级)
    day01_计算机的基础介绍
    第一章/第二章课后习题
    day12
    day11
    day09-10
    day08
    day07
  • 原文地址:https://www.cnblogs.com/yoyoketang/p/16664482.html
Copyright © 2020-2023  润新知