异步邮件发送项目目录结构
异步发送邮件/ ├── image.png ├── app.py ├── celery_config.py ├── tasks.py └── templates/ └── index.html
项目文件说明:
-
根目录 (
异步发送邮件/)-
image.png- 项目相关图片 -
app.py- Flask应用主文件,包含路由和视图函数 -
celery_config.py- Celery配置,设置消息代理和结果后端 -
tasks.py- 定义Celery任务,包含邮件发送功能
-
-
templates目录
-
index.html- 邮件发送前端页面,包含表单和JavaScript
-
技术栈说明:
-
Flask - Web框架,处理HTTP请求
-
Celery - 分布式任务队列,处理异步邮件发送
-
Redis - 作为Celery的消息代理和结果后端
-
Flask-Mail - 邮件发送扩展
-
HTML/CSS/JavaScript - 前端用户界面
这个项目实现了异步邮件发送功能,用户在前端填写表单后,任务会被提交到Celery队列异步处理,前端通过轮询方式检查任务状态。
步骤
1.先写异步的配置文件
# 创建一个celery_config.py 文件
class CeleryConfig:
# 使用 Redis 作为消息代理 :6379 是redis默认端口号
broker_url = 'redis://localhost:6380/0'
# 任务结果存储(禁用可提升性能)
result_backend = 'redis://localhost:6380/1'
# 定时任务配置(可选)
beat_schedule = {
'every-30-seconds': {
'task': 'tasks.periodic_task',
'schedule': 30.0, # 每30秒执行
},
}
2.写请求页面路由
from flask import Flask,render_template,jsonify,request
from tasks import celery,send_async_email
app = Flask(__name__)
# 请求页面路由
@app.route('/')
def index():
return render_template('index.html')
3.写请求页面的html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>邮件发送</title>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
padding: 20px;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
.container {
background-color: white;
border-radius: 10px;
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.15);
padding: 30px;
width: 100%;
max-width: 500px;
}
h1 {
text-align: center;
margin-bottom: 25px;
color: #2c3e50;
font-size: 28px;
padding-bottom: 10px;
border-bottom: 2px solid #eee;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 8px;
font-weight: 600;
color: #34495e;
font-size: 16px;
}
input, textarea {
width: 100%;
padding: 14px;
border: 1px solid #ddd;
border-radius: 6px;
font-size: 16px;
transition: all 0.3s ease;
}
input:focus, textarea:focus {
outline: none;
border-color: #3498db;
box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.2);
}
textarea {
min-height: 180px;
resize: vertical;
}
button {
background: linear-gradient(to right, #3498db, #2980b9);
color: white;
border: none;
border-radius: 6px;
padding: 15px;
font-size: 18px;
cursor: pointer;
width: 100%;
transition: all 0.3s ease;
font-weight: 600;
letter-spacing: 1px;
margin-top: 10px;
}
button:hover {
background: linear-gradient(to right, #2980b9, #3498db);
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
}
.note {
text-align: center;
margin-top: 20px;
color: #7f8c8d;
font-size: 14px;
border-top: 1px solid #eee;
padding-top: 15px;
}
@media (max-width: 600px) {
.container {
padding: 20px;
}
h1 {
font-size: 24px;
}
input, textarea {
padding: 12px;
}
}
</style>
</head>
<body>
<div class="container">
<h1>发送邮件</h1>
<form id="emailForm">
<div class="form-group">
<label for="email">收件邮箱:</label>
<input type="email" id="email" name="email" required placeholder="请输入收件人邮箱地址">
</div>
<div class="form-group">
<label for="subject">邮件主题:</label>
<input type="text" id="subject" name="subject" required placeholder="请输入邮件主题">
</div>
<div class="form-group">
<label for="content">邮件内容:</label>
<textarea id="content" name="content" required placeholder="请输入邮件内容"></textarea>
</div>
<button type="submit">发送邮件</button>
</form>
<div class="note">
注意:这是一个前端演示页面,实际发送功能需要后端支持。
</div>
</div>
</body>
</html>
4.写POST请求的send路由
from flask import Flask,render_template,jsonify,request
from tasks import celery,send_async_email
app = Flask(__name__)
# 请求页面路由
@app.route('/')
def index():
return render_template('index.html')
# 发送邮件路由
@app.route('/send',methods=["POST"])
def send_email():
# 获取请求的数据
data = request.form if request.form else request.get_json()
email = data['email']
subject = data['subject']
content = data['content']
…………………………………………
…………………………………………
…………………………………………
5.写异步任务文件tasks.py
from celery import Celery
from flask import Flask
from flask_mail import Mail, Message
# 初始化Celery实例
celery = Celery(__name__)
# 导入异步配置文件
celery.config_from_object('celery_config.CeleryConfig')
def create_app():
app = Flask(__name__)
# 邮件服务器配置(以网易邮箱为例)
app.config['MAIL_SERVER'] = 'smtp.163.com' # SMTP服务器地址
app.config['MAIL_PORT'] = 465 # SSL加密端口
app.config['MAIL_USE_SSL'] = True # 启用SSL加密
app.config['MAIL_USERNAME'] = '19086325659@163.com' # 发件邮箱
app.config['MAIL_PASSWORD'] = 'hsbbh' # 邮箱授权码(非密码)
app.config['MAIL_DEFAULT_SENDER'] = '19086325659@163.com' # 默认发件人
return app
flask_app = create_app()
mail = Mail(flask_app)
# 发送邮件的方法
def send_email(to, subject, content, **kwargs):
msg = Message(subject, recipients=[to])
msg.body = content # 纯文本内容
mail.send(msg)
# max_retries限制任务最低重试3次
@celery.task(bind=True,max_retries=3)
def send_async_email(self,email,subject,content):
try:
with flask_app.app_context():
send_email(
to=email,
subject=subject,
content=content
)
return f'邮件成功发送至{email}'
# 如果发送错误则重试,并且60秒重试一次,最多3次
except Exception as e:
self.retry(exc = e,countdown=60)
6.继续补充send路由
from flask import Flask,render_template,jsonify,request
from tasks import celery,send_async_email
app = Flask(__name__)
# 请求页面路由
@app.route('/')
def index():
return render_template('index.html')
# 发送邮件路由
@app.route('/send',methods=["POST"])
def send_email():
# 获取请求的数据
data = request.form if request.form else request.get_json()
email = data['email']
subject = data['subject']
content = data['content']
# 触发异步任务
task = send_async_email.delay(email,subject,content)
return jsonify({
'status':'邮件任务已提交',
'task_id':task.id
})
7.写查看任务状态路由
from flask import Flask,render_template,jsonify,request
from tasks import celery,send_async_email
app = Flask(__name__)
# 请求页面路由
@app.route('/')
def index():
return render_template('index.html')
# 发送邮件路由
@app.route('/send',methods=["POST"])
def send_email():
# 获取请求的数据
data = request.form if request.form else request.get_json()
email = data['email']
subject = data['subject']
content = data['content']
# 触发异步任务
task = send_async_email.delay(email,subject,content)
return jsonify({
'status':'邮件任务已提交',
'task_id':task.id
})
# 查看状态路由
@app.route('/status/<task_id>')
def task_status(task_id):
# 获取任务状态
task_result = celery.AsyncResult(task_id)
return jsonify({
"status": task_result.status,
"result": task_result.result if task_result.ready() else None
})
if __name__ == '__main__':
app.run(debug=True)
8.写index.html页面的js
<script>
const emailForm = document.querySelector('#emailForm')
emailForm.addEventListener('submit', (e) => {
e.preventDefault()
// 获取表单数据
const email = document.querySelector('#email').value
const subject = document.querySelector('#subject').value || '默认主题'
const content = document.querySelector('#content').value || '这是一封测试邮件'
// 给用户提示
const note = document.querySelector('.note')
note.innerHTML = '邮件正在发送中……'
// 发送POST请求
fetch('/send',{
method:'POST',
headers:{
'Content-Type':'application/x-www-form-urlencoded'
},
body:`email=${email}&subject=${subject}&content=${content}`
})
.then(res => res.json())
.then(data => {
if (data.task_id){
// console.log(data.task_id)
// 开始检查任务
checkTaskStatus(data.task_id)
}
}).catch(err => {
note.innerHTML = `网络错误:${err.message}`
})
})
function checkTaskStatus(taskid){
const note = document.querySelector('.note')
const checkTask = () =>{
fetch(`/status/${taskid}`)
.then(res => res.json())
.then(data => {
if(data.status == 'SUCCESS'){
note.innerHTML = '邮件发送成功'
}else if(data.status == 'FAILURE'){
note.innerHTML = '邮件发送失败'
}else if(data.status == 'PENDING'){
note.innerHTML = '邮件发送中'
setTimeout(checkTask,2000)
}else if(data.status == 'RETRY'){
note.innerHTML = '尝试重连中'
setTimeout(checkTask,3000)
}else {
note.innerHTML = '处理中'
setTimeout(checkTask,3000)
}
})
.catch(err =>{
note.innerHTML = '出错啦'
})
}
checkTask()
}
</script>
9.启动服务并进行调试
终端1 - 启动Redis
redis-server
终端2 - 启动Celery worker
celery -A tasks.celery worker --loglevel=info
终端3 - 启动Flask应用
python app.py

814

被折叠的 条评论
为什么被折叠?



