Flask开发(十五)Flask数据交互 使用Flask上传文件

内容纲要

本篇导读:

  • 上传文件简单使用
  • 上传文件进阶

在Web开发时,经常需要实现文件上传的功能。可以以普通方式进行文件的上传,上传过程中一般要检查上传的文件格式时是否符合要求,文件保存时要注意绝对路径和相对路径问题。

使用Flask上传文件的简单实现

Flask文件上传比较简单,需要有一下三点要求:

  • 一个<form>标签被标记有enctype=multipat/form-data,并且在里面包含一个<input type=file>标签。
  • 服务端应用通过请求对象上的files字典访问文件。
  • 使用文件的save()方法将文件永久地保存在文件系统上的某处。
from flask import Flask,render_template,request
import os
from os import path
from werkzeug.utils import secure_filename

os.path方法下相关属性如下:

  • os.path.sep:Windows下路径分隔符‘\’
  • os.path.sep:linux下路径分隔符‘/’
  • 根目录:os.path.curdir
  • 父目录:os.path.abspath(path)
  • 绝对路径:os.path.join()

在Windows下的路径分隔符和Linux下的路径分隔符是不一样的,当直接使用绝对路径时,跨平台会弹出No Such file or diretory的异常提示。

from werkzeug.utils import secure_filename:导入secure_filename方法,将中文文件名称传给secure_filename方法时所有的中文名都会被过滤掉,只剩下文件后缀名。

@app.route('/',methods=('GET','POST'))
def hello_world():
    if request.method == 'GET':
        return render_template('upload.html')
    else:
        f=request.files['file']
        filename = secure_filename(f.filename)
        f.save(path.join('static/uploads',filename))
        return '文件上传成功'

if __name__ == '__main__':
    app.run(debug=True)

下面结合上面的阐述,给出一个文件上传的示例:

app.py文件的内容如下:

from flask import Flask,render_template,request
from os import path
from werkzeug.utils import secure_filename

app = Flask(__name__)


@app.route('/',methods=('GET','POST'))
def hello_world():
    if request.method == 'GET':
        return render_template('upload.html')
    else:
        f=request.files['file']
        filename = secure_filename(f.filename)
        f.save(path.join('static/uploads',filename))
        return '文件上传成功'


if __name__ == '__main__':
    app.run(debug=True)

upload.html的文件内容如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
    <style type="text/css">
        .div1{
            height: 180px;
            width: 380px;
            border: 1px solid #8A8989;
            margin: 0 auto;
        }
        .input{
            display: block;
            width: 250px;
            height: 30px;
            margin: 10px auto;
        }
        .button{
            background: #2066c5;
            color: white;
            font-size: 18px;
            font-weight: bold;
            height: 30px;
            border-radius: 4px;
        }
    </style>
</head>
<body>
    <div class="div1">
        <form action="" method="post" enctype="multipart/form-data">
            <input type="file" name="file" class="input">
            <input type="submit" value="上传" class="input button">
        </form>
    </div>
</body>
</html>

运行上面程序之前,请先确保根目录下的static目录下已经创建好uploads目录,然后就可以运行本工程代码了。本工程可以实现上传任何文件,但是以中文命名的文件上传时,会出现中文名的丢失。

上传文件进阶

上面的示例代码中,实现了基本的文件上传功能,但是存在一些问题:

  • 文件没有重新命名,多个用户可能会存在上传同名文件的问题;
  • 没有实现文件目录的自动创建,如果网站部署时忘记创建文件保存目录,则会出现文件保存失败问题,影响用户体验;
  • 文件上传时,没有进行必要的文件格式检验,用户可以直接将可执行文件上传到服务器上,影响到服务器的数据安全性。

改进app.py文件内容:

import time

from flask import Flask,render_template,request
import os
from os import path

from werkzeug.datastructures import CombinedMultiDict
from werkzeug.utils import secure_filename, send_from_directory
import platform
from form import UploadForm

app = Flask(__name__)
if platform.system() == 'Windows':
    slash='\\'
else:
    slash='/'
UPLOAD_PATH = os.path.curdir+slash+'uploads'+slash

@app.route('/',methods=('GET','POST'))
def hello_world():
    if request.method == 'GET':
        return render_template('upload.html')
    else:
        if not os.path.exists(UPLOAD_PATH):
            os.makedirs(UPLOAD_PATH)
        form = UploadForm(CombinedMultiDict([request.form,request.files]))
        if form.validate():
            f = request.files['file']
            filename = secure_filename(f.filename)
            ext = filename.rsplit('.',1)[1]
            unix_time = int(time.time())
            new_filename = str(unix_time)+'.'+ext

            file_url=UPLOAD_PATH+new_filename
            f.save(path.join(UPLOAD_PATH,new_filename))
        else:
            return '只支持jpg、png以及gif格式的文件!'
        return '文件上传成功'

@app.route('/images/<filename>/',methods=('GET','POST'))
def get_image(filename):
    dirpath = os.path.join(app.root_path,'uploads')
    # return send_from_directory(dirpath,filename,environ={'REQUEST_METHOD':'GET'},as_attachment=True) # 为下载方式
    return send_from_directory(dirpath,filename,environ={'REQUEST_METHOD':'GET'}) #为浏览方式

if __name__ == '__main__':
    app.run(debug=True)

 

新建fom.py文件,内容如下:

from wtforms import Form,FileField
from flask_wtf.file import FileRequired,FileAllowed

class UploadForm(Form):
    file=FileField(validators=[FileRequired(),FileAllowed(['jpg','png','gif'])])

FileRequired用来验证文件上传是否为空。FileAllowed用来验证上传文件的后缀名。

效果如下:

阅读剩余
THE END