任务 5 配置基于CVM的Web项目
任务目的
经过 任务3 的操作。学员已经掌握了使用MyQR生成个性二维码的主要流程。但是上面的实验仅能在本地机器上进行操作,且无法分享给他人进行尝试。
如果希望制作一个简单的应用,将二维码的生成工作在网页上实现,并且可以供连接互联网的所有用户访问,可以参考这一步的任务操作:构建一个Flask项目,并配置在云服务器CVM上运行。
完成这一步的操作后,用户可以直接通过互联网访问生成二维码的应用页面,实现图片二维码的生成和下载操作。
任务步骤
1.登录CVM
打开浏览器,进入 CVM实例控制台 页面,选择已经配置好的CentOS云服务器实例,进行登录。
注:关于登录的具体操作,可以参考 任务1 中的 “登录CVM” 一节。
2.创建项目目录,复制项目代码
首先需要清楚项目的完整目录,以及目录中各文件和文件夹的作用。下面展示的是整个Flask项目的完整目录,以及项目中各文件和目录的作用说明。
.
├── app.py # 主程序,项目启动文件
├── static # 用于存放生成图片的静态文件目录
│ ├── export # 存放导出的图片文件
│ └── origin # 存放原始的图片文件
└── templates # 存放模板文件的目录
└── index.html # 展示程序主页的模板文件
接下来将会通过具体的操作构建一个同样的目录,并填充文件内部的功能代码。
(1)创建一个项目目录,指定一个项目名。本例中定义一个名为qr_export
的项目目录,并跳转到此目录中。
mkdir qr_export && cd qr_export
(2)创建static
目录,以及它的两个子目录static/origin
和static/export
,分别用于存放原始图片和导出图片:
mkdir -p static/origin static/export
注:静态文件一般指项目中的CSS、js和图片文件。静态文件一般放到一个单独的目录中,以方便管理。此例中对应的静态文件目录为
static
目录,这也是Web项目常用的静态文件目录。
(3)创建项目文件app.py
vim app.py
此时将进入Vim终端交互界面,界面展示效果如下:
接下来需要在文件内粘贴代码。
英文输入状态下按I
键(此处大小写均可)进入编辑模式,接下来复制下方的代码,在浏览器中通过 “右键-粘贴” 将完整的代码粘贴到文件中。
# flask模块用于实现简单的web服务项目
from flask import Flask, render_template, request, flash
# 导入COS相关模块,用于将二维码存入腾讯云对象存储COS
from qcloud_cos import CosConfig
from qcloud_cos import CosS3Client
import sys
# MyQR模块用于实现二维码的制作功能
from MyQR import myqr
# time模块用于生成文件中的时间戳
import time
# 导入配置文件
from config import *
app = Flask(__name__)
# 定义一个secret_key,内容可以随意指定,用于混入消息中进行加密
app.secret_key = "secret_key" # 用户的 SecretKey,建议使用子账号密钥,授权遵循最小权限指引,降低使用风险。子账号密钥获取可参考https://cloud.tencent.com/document/product/598/37140
# ---实现主页展示功能---
@app.route('/')
def index():
# 展示渲染后的主页模板文件
return render_template('index.html')
# ---实现主页展示功能---
# ---实现二维码导出功能---
@app.route("/export", methods=["POST"])
def export():
"""上传文件函数,用于执行文件上传功能"""
# 判断请求类型,只对POST请求的操作进行响应
if request.method == "POST":
# 获取表单中的 url_str 对应的内容
url_str = request.form.get('url_str')
# 如果 url_str 为空,默认使用腾讯云首页
if not url_str:
url_str = "https://cloud.tencent.com/"
# 获取提交的文件对象,此文件通过模板中的表单提交
upload_file = request.files.get('file')
# 创建用于区分文件的13位时间戳,此时间戳会拼接在每个文件名的尾部
time_str = str(int(round(time.time() * 1000)))
# 如果没有获取到上传文件
if not upload_file:
# 拼接导出路径,文件名添加时间戳避免读取缓存
export_name = "qrcode_{}.png".format(time_str)
export_path = "./static/export/{}".format(export_name)
# 生成不含图片的普通二维码
try:
myqr.run(url_str, save_name=export_path)
# 如果生成过程中报错,展示错误信息,并返回主页
except Exception as e:
flash("Error: {}".format(e))
return index()
# 定义展示图片的标识 show_photo 为True,模板页面将会展示二维码
show_photo = True
# 如果上传了图片,保存图片并生成图片二维码
else:
# 获取文件名和文件后缀,用于进行判断和文件名生成
file_name = upload_file.filename
# 获取文件名的头部,作为生成的文件名头部
file_header = file_name.split(".")[0]
# 获取文件的格式尾缀,用于对文件格式进行判断
file_type = file_name[-4:]
print(file_type)
# 如果文件后缀不符,展示提示信息,并跳转到主页
if file_type not in [".bmp", ".jpg", ".png", ".gif"]:
# flash可以在模板文件中闪现推送提示信息
flash("文件格式有误,支持的文件格式为:bmp, jpg, png, gif")
# 不进行转换操作,直接返回主页
return index()
# 定义上传图片的原始路径,用于存储上传的原始路径图片
save_path = "./static/origin/{}".format(upload_file.filename)
# 保存上传的原始图片
upload_file.save(save_path)
# 拼接导出文件的文件名头部,尾缀将在稍后进行判断
export_header = "{}_{}".format(file_header, time_str)
# 拼接导出路径,文件名添加时间戳避免读取缓存
if file_type in [".bmp", ".jpg", ".png"]:
export_name = export_header + ".png"
export_path = "./static/export/{}".format(export_name)
elif file_type == ".gif":
export_name = export_header + ".gif"
export_path = "./static/export/{}".format(export_name)
# 生成包含图片的二维码(此处定义为生成彩色二维码)
try:
myqr.run(url_str,
picture=save_path,
colorized=True,
save_name=export_path)
# 如果生成过程中报错,展示错误信息,并返回主页
except Exception as e:
flash("Error: {}".format(e))
return index()
# 定义展示图片的标识 show_photo 为True,模板页面将会展示二维码
show_photo = True
# 完成上传后,重新渲染模板页面
return render_template('index.html', export_path=export_path, show_photo=show_photo, export_name=export_name)
# ---实现二维码导出功能---
# ---实现二维码图片的下载功能---
@app.route("/download/<file_name>")
def download(file_name):
download_type = request.args.get("type")
if download_type == "local":
from flask import Response # 导入Response模块
with open("./static/export/{}".format(file_name), "rb") as f:
fp = f.read()
response = Response(fp, content_type='application/octet-stream')
# file_name需要进行编码转换,否则中文文件无法正常下载
response.headers["Content-disposition"] = 'attachment; filename={}'.format(file_name.encode("utf-8").decode("latin-1"))
return response
elif download_type == "cos":
global secret_id, secret_key, region
# 执行功能——上传图片到COS存储桶
secret_id = secret_id # 用户的 SecretId,建议使用子账号密钥,授权遵循最小权限指引,降低使用风险。子账号密钥获取可参考https://cloud.tencent.com/document/product/598/37140
secret_key = secret_key # 用户的 SecretKey,建议使用子账号密钥,授权遵循最小权限指引,降低使用风险。子账号密钥获取可参考https://cloud.tencent.com/document/product/598/37140
region = region
config = CosConfig(Region=region, SecretId=secret_id,
SecretKey=secret_key) # 用户的 SecretKey,建议使用子账号密钥,授权遵循最小权限指引,降低使用风险。子账号密钥获取可参考https://cloud.tencent.com/document/product/598/37140
client = CosS3Client(config)
with open("./static/export/{}".format(file_name), "rb") as fp:
try:
response = client.put_object(
Bucket=bucket_name,
Body=fp,
Key=file_name,
StorageClass='STANDARD',
EnableMD5=False
)
except Exception as e:
# 上传文件失败时触发
flash("Error: {}".format(e))
else:
flash("添加成功!COS图片链接为: https://{}.cos.{}.myqcloud.com/{}".format(bucket_name, region, file_name))
export_path = "/static/export/{}".format(file_name)
export_name = file_name
show_photo = True
return render_template('index.html', export_path=export_path, show_photo=show_photo, export_name=export_name)
# ---实现二维码图片的下载功能---
if __name__ == "__main__":
# 定义监听host为0.0.0.0,表示此服务可以被外部网络访问
# 默认监听端口为5000
app.run(host="0.0.0.0")
项目核心功能说明:
此项目中主要包含三个视图函数,分别用于执行主页展示、生成二维码和下载功能,下面依次对这三个视图函数进行简单介绍:
视图函数名 | url地址 | 实现功能 |
---|---|---|
index |
| 执行页面中所有内容的展示功能 |
export |
| 执行二维码的生成功能,会根据用户输入进行二维码处理 |
download |
| 执行下载已生成二维码的,将二维码图片添加到COS并生成访问链接的功能 |
完成粘贴后,需要保存文件,按下ESC
键切换到命令模式,此模式下可以向编辑器中输入命令。在英文模式下输入:wq
,即可保存文件,并退出Vim编辑器。
(4)创建参数配置文件config.py
操作类似上方创建项目文件app.py
的流程。
首先创建参数配置文件:
vim config.py
按下I
键进入编辑模式,复制下方的代码,粘贴到文件中:
bucket_name = "<你的bucket_name>"
region = "<你的region>"
secret_id = "<你的secret_id>" # 用户的 SecretId,建议使用子账号密钥,授权遵循最小权限指引,降低使用风险。子账号密钥获取可参考https://cloud.tencent.com/document/product/598/37140
secret_key = "<你的secret_key>" # 用户的 SecretKey,建议使用子账号密钥,授权遵循最小权限指引,降低使用风险。子账号密钥获取可参考https://cloud.tencent.com/document/product/598/37140
将代码中右侧的部分,替换为学员自己的存储桶信息和云API密钥信息。
注:具体的参数获取,可以参考 任务4 中 “获取存储桶信息” 和 “获取云API密钥” 这两节
完成参数的配置后,再次按下 ESC
键切换到命令模式,并在英文模式下使用命令:wq
保存文件并退出编辑器。
(5)创建模板文件目录
此处将创建名为templates
的模板文件目录,并在创建完成后跳转到此目录中。
mkdir templates && cd templates
(6)创建模板文件index.html
首先创建模板文件:
vim index.html
按下I
键进入编辑模式,复制下方的代码,粘贴到文件中。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>图片二维码生成系统</title>
<!-- 导入jquery,用于作为bootstrap依赖环境 -->
<script src="https://code.jquery.com/jquery-3.4.1.slim.min.js"
integrity="sha256-pasqAKBDmFT4eHoN2ndd6lN370kFiGUFyTiUHWhU7k8="
crossorigin="anonymous"></script>
<!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u"
crossorigin="anonymous">
</head>
<body>
<div class="container">
<h1>图片二维码生成系统</h1>
<hr />
<form class="form-inline" action="/export" method="post"
enctype="multipart/form-data">
<div class="form-group url_con">
<h4>请输入访问url:</h4>
<input type="text" name="url_str">
</div>
<div class="form-group file_con">
<h4>请选择要上传的图片:</h4>
<input type="file" name="file">
</div>
<input type="submit" value="生成" class="btn btn-primary">
</form>
<hr />
{% if show_photo %}
<div class="hide_con">
<div class="qrcode_con">
<img class="qrcode" src="{{ export_path }}" alt="生成的二维码">
</div>
<div class="download_con">
<a class="download_button" href="/download/{{ export_name }}?type=local">
<button class="download_button btn btn-success">下载</button>
</a>
<a class="download_button" href="/download/{{ export_name }}?type=cos">
<button class="download_button btn btn-info">添加到COS</button>
</a>
</div>
</div>
{% endif %}
<!-- 用于展示提示信息 -->
{% for message in get_flashed_messages() %}
<strong style="color:red">{{ message }}</strong>
{% endfor %}
</div>
<style type="text/css">
.container{
width: 50%;
margin: 40px auto 0;
}
h1 {
text-align: center;
}
.url_con {
width: 35%;
}
.file_con {
width: 45%;
margin-top: -2px;
}
.hide_con {
text-align: center;
}
.qrcode_con {
width: 100%;
}
.qrcode {
display: inline-block;
width: 200px;
height: 200px;
border: 1px solid #000;
}
.download_con {
width: 60%;
margin: 10px 20%;
display: flex;
}
.download_button {
width: 55%;
}
</style>
</body>
</html>
完成粘贴后,再次按下 ESC
键切换到命令模式,并在英文模式下使用命令:wq
保存文件并退出编辑器。
至此完整的项目目录及文件内容均已完成配置,但目前所在的位置仍为模板文件目录templates
下,通过命令cd ..
跳转到上一级目录——项目的根目录中,方便在下一步进行运行项目的操作。
3.运行项目
(1)在命令行执行命令python3 app.py
,运行成功后会展示如下提示信息:
* Serving Flask app "app" (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: off
* Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
注意:提示信息中也有提到,这个服务器是一个用于开发的服务器,不要将它用于生产环境。 Flask自带服务器的处理能力极其有限,仅适用于测试使用。本例为简化步骤直接自带服务器用于进行测试。如果配置正式的生产环境,需要搭建一个专门的Web服务器。
(2)在浏览器中输入服务器的IP和项目端口,尝试访问项目页面。
注:IP可以在登录CVM的登录界面获取,Flask项目的默认端口为5000。 如CVM的IP为
1.1.1.1
,没有额外配置Flask端口,访问地址1.1.1.1:5000
即可进入项目页面
如果访问成功,将会看到以下界面:
接下来可以尝试进行二维码的生成操作,确认项目功能已经顺利实现。
学员评价