# 文件上传与邮件发送
### 原生实现
- 模板文件
```html
<form method="post" enctype="multipart/form-data">
<input type="file" name="photo" /><br />
<input type="submit" value="上传" />
</form>
```
- 视图函数
```python
import os
# 配置上传文件保存目录
app.config['UPLOADED_FOLDER'] = os.path.join(os.getcwd(), 'static/upload')
@app.route('/upload/', methods=['GET', 'POST'])
def upload():
if request.method == 'POST':
# 获取上传对象
photo = request.files.get('photo')
if photo:
# 拼接保存路径名
pathname = os.path.join(app.config['UPLOADED_FOLDER'], photo.filename)
# 保存上传文件
photo.save(pathname)
return '上传成功'
else:
return '上传失败'
return render_template('upload.html')
```
- 上传限制设置
```python
# 允许上传的文件后缀
ALLOWED_SUFFIX = set(['png', 'jpg', 'jpeg', 'gif'])
# 判断是否是允许的后缀
def allowed_file(filename):
return '.' in filename and filename.rsplit('.', 1)[1] in ALLOWED_SUFFIX
# 限制请求大小
app.config['MAX_CONTENT_LENGTH'] = 1024 * 1024 * 8
# 展示上传的图片
@app.route('/uploaded/<filename>')
def uploaded(filename):
return send_from_directory(app.config['UPLOADED_FOLDER'], filename)
@app.route('/upload/', methods=['GET', 'POST'])
def upload():
img_url = None
if request.method == 'POST':
# 获取上传对象
photo = request.files.get('photo')
if photo and allowed_file(photo.filename):
# 拼接保存路径名
pathname = os.path.join(app.config['UPLOADED_FOLDER'], photo.filename)
# 保存上传文件
photo.save(pathname)
# 构造上传文件的url
img_url = url_for('uploaded', filename=photo.filename)
return render_template('upload.html', img_url=img_url)
```
### flask-uploads
- 说明:极大的的简化了文件上传相关的操作,使用非常方面。
- 安装:`pip install flask-uploads`
- 使用:
- 配置
```python
from flask_uploads import UploadSet, IMAGES
from flask_uploads import configure_uploads
from flask_uploads import patch_request_class
import os
app.config['UPLOADED_PHOTOS_DEST'] = os.getcwd()
app.config['MAX_CONTENT_LENGTH'] = 8 * 1024 * 1024
# 创建上传对象
photos = UploadSet('photos', IMAGES)
# 配置上传对象
configure_uploads(app, photos)
# 配置上传文件大小,默认为64M,
# 若设置为None,则以MAX_CONTENT_LENGTH配置为准
patch_request_class(app, size=None)
```
- 视图函数
```python
@app.route('/upload/', methods=['GET', 'POST'])
def upload():
img_url = None
if request.method == 'POST':
# 获取上传对象
photo = request.files.get('photo')
if photo:
# 保存上传文件,返回文件名
filename = photos.save(photo)
# 根据文件名获取上传文件的URL
img_url = photos.url(filename)
return render_template('upload.html', img_url=img_url)
```
### 综合使用
- 要求:结合flask-bootstrap、flask-wtf、flask-uploads等完成文件上传
- 使用:
- 配置
```python
from flask_wtf import FlaskForm
from flask_wtf.file import FileField, FileAllowed, FileRequired
from wtforms import SubmitField
from flask_uploads import UploadSet, IMAGES
from flask_uploads import configure_uploads
from flask_uploads import patch_request_class
from flask_bootstrap import Bootstrap
import os
bootstrap = Bootstrap(app)
app.config['SECRET_KEY'] = '123456'
app.config['MAX_CONTENT_LENGTH'] = 8 * 1024 * 1024
app.config['UPLOADED_PHOTOS_DEST'] = os.path.join(os.getcwd(), 'static/upload')
photos = UploadSet('photos', IMAGES)
configure_uploads(app, photos)
patch_request_class(app, size=None)
class UploadForm(FlaskForm):
photo = FileField('头像', validators=[FileRequired(message='请选择文件'),
FileAllowed(photos, message='只能上传图片文件')])
submit = SubmitField('上传')
```
- 视图函数
```python
@app.route('/upload/', methods=['GET', 'POST'])
def upload():
img_url = None
form = UploadForm()
if form.validate_on_submit():
photo = form.photo.data
filename = photos.save(photo)
img_url = photos.url(filename)
return render_template('upload.html', form=form, img_url=img_url)
```
- 模板文件
```html
{% extends 'bootstrap/base.html' %}
{% from 'bootstrap/wtf.html' import quick_form %}
{% block title %}完整的文件上传{% endblock %}
{% block content %}
<div class="container">
{% if img_url %}
<img src="{{ img_url }}">
{% endif %}
{{ quick_form(form) }}
</div>
{% endblock %}
```
- 生成随机文件名
```python
def random_string(length=32):
import random
base_str = 'abcdefghijklmnopqrstuvwxyz1234567890'
return ''.join(random.choice(base_str) for i in range(length))
@app.route('/upload/', methods=['GET', 'POST'])
def upload():
。。。
# 提取文件后缀
suffix = os.path.splitext(photo.filename)[1]
# 生成随机文件名
filename = random_string() + suffix
# 保存文件
photos.save(photo, name=filename)
。。。
```
- 生成缩略图:PIL模块(只支持py2,要支持py3需要安装pillow)
```python
from PIL import Image
@app.route('/upload/', methods=['GET', 'POST'])
def upload():
...
# 拼接完整文件路径名
pathname = os.path.join(app.config['UPLOADED_PHOTOS_DEST'], filename)
# 打开文件
img = Image.open(pathname)
# 设置大小
img.thumbnail((64, 64))
# 保存图片
img.save(pathname)
...
```
### flask-mail
- 说明:专门用于邮件发送的扩展库,使用非常方便。
- 安装:`pip install flask-mail`
- 使用:
```python
from flask_mail import Mail, Message
import os
# 邮件发送配置,一定要放在创建Mail对象之前
app.config['MAIL_SERVER'] = 'smtp.1000phone.com'
# 用户名
app.config['MAIL_USERNAME'] = 'lijie@1000phone.com'
# 密码
app.config['MAIL_PASSWORD'] = os.getenv('MAIL_PASSWORD', '123456')
# 创建发送邮件的对象
mail = Mail(app)
@app.route('/send/')
def send():
# 创建邮件消息对象
msg = Message('账户激活',
recipients=['shuai_fmzj@163.com'],
sender=app.config['MAIL_USERNAME'])
msg.html = '恭喜你,中奖了!!!'
# 发送邮件
mail.send(msg)
return '邮件已发送'
```
- 封装函数发送邮件
```python
def send_mail(subject, to, template, *args, **kwargs):
if isinstance(to, list):
recipients = to
elif isinstance(to, str):
recipients = to.split(',')
else:
raise Exception('邮件接收者参数类型有误')
# 创建邮件消息对象
msg = Message(subject,
recipients=recipients,
sender=app.config['MAIL_USERNAME'])
# 将邮件模板渲染后作为邮件内容
msg.html = render_template(template, *args, **kwargs)
# 发送邮件
mail.send(msg)
```
- 异步发送邮件
```python
from flask import current_app
# 异步发送邮件任务
def async_send_mail(app, msg):
# 邮件发送必须在程序上下文
# 新的线程中没有上下文,因此需要手动创建
with app.app_context():
mail.send(msg)
# 封装函数发送邮件
def send_mail(subject, to, template, *args, **kwargs):
if isinstance(to, list):
recipients = to
elif isinstance(to, str):
recipients = to.split(',')
else:
raise Exception('邮件接收者参数类型有误')
# 创建邮件消息对象
msg = Message(subject,
recipients=recipients,
sender=app.config['MAIL_USERNAME'])
# 将邮件模板渲染后作为邮件内容
msg.html = render_template(template, *args, **kwargs)
# 异步发送邮件
# current_app是app的代理对象
# 根据代理对象current_app找到原始的app
app = current_app._get_current_object()
# 创建线程
thr = Thread(target=async_send_mail, args=(app, msg))
# 启动线程
thr.start()
# 返回线程
return thr
```
###环境变量
- windows:
- 设置:`set 环境变量名=值`
- 获取:`set 环境变量名`
- linux:
- 导出:`export 环境变量名=值`
- 获取:`echo $环境变量名`
- 代码:
- `os.getenv('环境变量名', '123456')`