https://zhuanlan.zhihu.com/p/23731819
Flask文件上传(一):原生实现
李辉
greyli.com
87 人赞同了该文章
文件上传是个躲不掉的问题,用户头像,文章图片,文件分享等等都需要上传功能。但这里涉及很多内容,上传文件,过滤文件类型,限制大小,上传前的编辑筛选,拖拽上传,进度条,文件命名,文件目录管理,访问速度……
尽管有一些库和插件来简化这个过程,但还是很麻烦,如果没有太多时间和精力,或是有很大的上传需求,就不要跳坑了。这一切可以花钱解决:文件可以用各种网盘;图片使用各种图床,速度快而且功能齐全,你只需要集成上传接口;
因为内容很多,我打算用三到四篇文章来写这个主题。这篇文章介绍用Flask原生实现上传和一些基础内容。
Flask文件上传系列目录
- Flask文件上传(一):原生实现
- Flask文件上传(二):使用扩展实现
- Flask文件上传(三):完整实现
- Flask文件上传(四):文件管理与多文件上传
- Flask文件上传(五):拖拽上传和进度条
使用Flask原生实现文件上传
简单来说,只有三个步骤:
1、创建一个上传表单:
<form method="POST" enctype="multipart/form-data"><input type="file" name="file"><input type="submit" value="Upload">
</form>
2、当按下提交键后,通过request对象上的files获取文件。和以前用request获取表单值一样,使用input字段的name值获取:
file = request.files['file']
3、使用save()方法保存文件,指定保存的地址及文件名:
file.save(path + filename)
当然,除了这些,还有很多东西要考虑。
上传配置
在这里我们设置上传文件夹地址、允许的文件扩展名、限制文件大小:
UPLOAD_FOLDER = '/path/to/the/uploads'
ALLOWED_EXTENSIONS = set(['txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'])
MAX_CONTENT_LENGTH = 16 * 1024 * 1024 # 16MB
你也可以使用:
app.config['UPLOAD_FOLDER'] = '/path/to/the/uploads'
Flask配置介绍见这篇文章。
安全问题
1、导入Werkzeug提供的secure_filename()函数来检查文件名:
filename = secure_filename(file.filename)
要注意的是,secure_filename仅返回ASCII字符。所以, 非ASCII(比如汉字)会被过滤掉,空格会被替换为下划线。你也可以自己处理文件名,或是在使用这个函数前将中文替换为拼音或是英文。具体见后续文章。
2、使用我们在上面配置的扩展名来检查文件类型。
创建一个检查函数:
def allowed_file(filename):return '.' in filename and \filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS
判断上传文件名:
...
if file and allowed_file(file.filename):
...
3、使用上面配置的文件最大长度来检查文件大小(仅需要配置),如果超过限制,会抛出RequestEntityTooLarge异常,进而返回413错误(在开发服务器可能会直接断开连接,属正常现象)。
获取上传后的文件
配置一个函数来获取上传文件的url:
from flask import send_from_directory@app.route('/uploads/<filename>')
def uploaded_file(filename):return send_from_directory(app.config['UPLOAD_FOLDER'],filename)
获取url:
file_url = url_for('uploaded_file', filename=filename)
完整的实现
这是一个图片上传Demo,你可以复制或是在Github上下载体验一下。
# -*- coding: utf-8 -*-
import os
from flask import Flask, request, url_for, send_from_directory
from werkzeug import secure_filenameALLOWED_EXTENSIONS = set(['png', 'jpg', 'jpeg', 'gif'])app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = os.getcwd()
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024html = '''<!DOCTYPE html><title>Upload File</title><h1>图片上传</h1><form method=post enctype=multipart/form-data><input type=file name=file><input type=submit value=上传></form>'''def allowed_file(filename):return '.' in filename and \filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS@app.route('/uploads/<filename>')
def uploaded_file(filename):return send_from_directory(app.config['UPLOAD_FOLDER'],filename)@app.route('/', methods=['GET', 'POST'])
def upload_file():if request.method == 'POST':file = request.files['file']if file and allowed_file(file.filename):filename = secure_filename(file.filename)file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))file_url = url_for('uploaded_file', filename=filename)return html + '<br><img src=' + file_url + '>'return htmlif __name__ == '__main__':app.run()
Gist地址:https://gist.github.com/greyli/a643aaac06ea8c23769c0c3d9ccaae79
相关链接
- request对象:http://flask.pocoo.org/docs/0.11/api/#incoming-request-data
- Flask文档,文件上传:http://flask.pocoo.org/docs/0.11/patterns/fileuploads/
- - - - -
更多关于Flask和Web开发的原创内容,欢迎关注知乎专栏 - Hello, Flask!。