现在,网站的整体架构(包括前端、后台)都搭建起来了,但是我们还有许多东西需要完善。
1.后台需要有修改公司介绍的功能。
2.需要有上传图片的功能。
下面我们就来完善这些功能。
一、添加公司介绍
运行pgAdmin连上数据库,然后按第4章的做法,打开sql查询分析器,运行下面代码添加一条数据库记录:
INSERT INTO infomation(id, title) VALUES (1, '公司介绍');
二、公司介绍记录读取
将main.py用户登录判断那两行代码注释掉

在api文件夹下面新增一个about.py,在下面添加如下代码:
#!/usr/bin/evn python
# coding=utf-8
from bottle import get, put
from common import web_helper, string_helper, db_helper
@get('/api/about/')
def callback():
"""
获取指定记录
"""
sql = """select * from infomation where id = 1"""
# 读取记录
result = db_helper.read(sql)
if result:
# 直接输出json
return web_helper.return_msg(0, '成功', result[0])
else:
return web_helper.return_msg(-1, "查询失败")
因为公司介绍id添加后不会再改变,所以sql语句直接绑死id为1,另外,执行数据库查询以后,返回的是列表,所以返回记录时要加上序号:result[0]
三、测试结果
在浏览器输入:http://127.0.0.1:9090/api/about/,看到如下界面,里面有“ "state": 0”的信息,就表示成功了。

备注:乱码问题
如果你看到的界面是这样的,是因为中文字符是unicode编码。

这样的代码可以通过站长工具进行转码。

四、防sql注入和防xss处理
首先使用pip install bleach安装bleach这个库,它是一个基于白名单、通过转义或去除标签和属性的方式,来对HTML文本净化的python库。
打开string_helper文件,在开头加入import bleach,在末尾添加如下代码:
def clear_xss(html):
"""
清除xss攻击标签
:param html: 要处理的html
:return:
"""
tags = ['a', 'abbr', 'acronym', 'b', 'blockquote', 'code', 'em', 'i', 'li', 'ol', 'strong', 'ul']
tags.extend(
['div', 'p', 'hr', 'br', 'pre', 'code', 'span', 'h1', 'h2', 'h3', 'h4', 'h5', 'del', 'dl', 'img', 'sub', 'sup', 'u',
'table', 'thead', 'tr', 'th', 'td', 'tbody', 'dd', 'caption', 'blockquote', 'section'])
attributes = {'*': ['class', 'id'], 'a': ['href', 'title', 'target'], 'img': ['src', 'style', 'width', 'height']}
return bleach.linkify(bleach.clean(html, tags=tags, attributes=attributes))
五、添加修改公司介绍接口
修改记录使用put方式接收:@put('/api/about/'),在about.py中,添加如下代码:
@put('/api/about/')
def callback():
"""
修改记录
"""
front_cover_img = web_helper.get_form('front_cover_img', '图片')
content = web_helper.get_form('content', '内容', is_check_special_char=False)
# 防sql注入攻击处理
content = string_helper.filter_str(content, "'")
# 防xss攻击处理
content = string_helper.clear_xss(content)
# 更新记录
sql = """update infomation set front_cover_img=%s, content=%s where id=1"""
vars = (front_cover_img, content,)
# 写入数据库
db_helper.write(sql, vars)
# 直接输出json
return web_helper.return_msg(0, '成功')
六、添加上传接口
由于我们使用的文本编辑器是百度的ueditor,因为它没有python的上传处理代码,所以我们需要动手编辑上传接口,以及html上也要进行对应的修改。
在api文件夹下面新建一个files.py,代码如下:
#!/usr/bin/evn python
# coding=utf-8
import os
from bottle import post, request
from common import datetime_helper, random_helper, log_helper
@post('/api/files/')
def callback():
"""
修改记录
"""
# 初始化输出值
result = {
"state": "FAIL",
"url": "",
"title": "上传失败",
"original": ""
}
# 获取上传文件
try:
# upfile为前端HTML上传控件名称
upload = request.files.get('upfile')
# 如果没有读取到上传文件或上传文件的方式不正确,则返回上传失败状态
if not upload:
return result
# 取出文件的名字和后缀
name, ext = os.path.splitext(upload.filename)
# 给上传的文件重命名,默认上传的是图片
if ext and ext != '':
file_name = datetime_helper.to_number() + random_helper.get_string(5) + ext
else:
file_name = datetime_helper.to_number() + random_helper.get_string(5) + '.jpg'
upload.filename = file_name
# 设置文件存储的相对路径
filepath = '/upload/' + datetime_helper.to_number('%Y%m%d') + '/'
# 组合成服务器端存储绝对路径
upload_path = os.getcwd() + filepath
# 如果目录不存在,则创建目录
if not os.path.exists(upload_path):
os.mkdir(upload_path)
# 保存文件
upload.save(upload_path + upload.filename, overwrite=True)
# 设置输出参数(返回相对路径给客户端)
result['title'] = result['original'] = upload.filename
result['url'] = filepath + upload.filename
result['state'] = 'SUCCESS'
except Exception as e:
log_helper.error('上传失败:' + str(e.args))
# 直接输出json
return result
七、建立上传文件的存放文件夹
在项目的根目录下创建upload文件夹。
八、创建文件下载路由
添加bottle库的response, static_file这两个包,打开main.py文件,将原来的:
from bottle import default_app, get, run, request, hook
改成:
from bottle import default_app, get, run, request, hook, route, response, static_file
在26行的位置插入以下代码:
# 定义upload为上传文件存储路径 upload_path = os.path.join(program_path, 'upload')
插入后的效果:

再在函数主入口前面位置添加以下代码,设置后只要放在upload目录下的文件都可以直接通过浏览器下载:
@get('/upload/<filepath:path>')
def upload_static(filepath):
"""设置静态内容路由"""
response.add_header('Content-Type', 'application/octet-stream')
return static_file(filepath, root=upload_path)
效果图:

九、修改前端页面
打开html文件夹中的/lib/ueditor/1.4.3/ueditor.config.js,修改“服务器统一请求接口路径”为下面的形式。
将原来的
, serverUrl: URL + "php/controller.php"
改成:
, serverUrl: "/api/files/"
在/lib/ueditor/1.4.3/目录下面添加一个python文件夹,在里面新增一个config.json文件,代码如下:
/* 前后端通信相关的配置,注释只允许使用多行方式 */
{
/* 上传图片配置项 */
"imageActionName": "uploadimage", /* 执行上传图片的action名称 */
"imageFieldName": "upfile", /* 提交的图片表单名称 */
"imageMaxSize": 2048000, /* 上传大小限制,单位B */
"imageAllowFiles": [".png", ".jpg", ".jpeg", ".gif", ".bmp"], /* 上传图片格式显示 */
"imageCompressEnable": true, /* 是否压缩图片,默认是true */
"imageCompressBorder": 1600, /* 图片压缩最长边限制 */
"imageInsertAlign": "none", /* 插入的图片浮动方式 */
"imageUrlPrefix": "", /* 图片访问路径前缀 */
"imagePathFormat": "/upload/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */
/* {filename} 会替换成原文件名,配置这项需要注意中文乱码问题 */
/* {rand:6} 会替换成随机数,后面的数字是随机数的位数 */
/* {time} 会替换成时间戳 */
/* {yyyy} 会替换成四位年份 */
/* {yy} 会替换成两位年份 */
/* {mm} 会替换成两位月份 */
/* {dd} 会替换成两位日期 */
/* {hh} 会替换成两位小时 */
/* {ii} 会替换成两位分钟 */
/* {ss} 会替换成两位秒 */
/* 非法字符 \ : * ? " < > | */
/* 具请体看线上文档: fex.baidu.com/ueditor/#use-format_upload_filename */
/* 涂鸦图片上传配置项 */
"scrawlActionName": "uploadscrawl", /* 执行上传涂鸦的action名称 */
"scrawlFieldName": "upfile", /* 提交的图片表单名称 */
"scrawlPathFormat": "/upload/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */
"scrawlMaxSize": 2048000, /* 上传大小限制,单位B */
"scrawlUrlPrefix": "", /* 图片访问路径前缀 */
"scrawlInsertAlign": "none",
/* 截图工具上传 */
"snapscreenActionName": "uploadimage", /* 执行上传截图的action名称 */
"snapscreenPathFormat": "/upload/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */
"snapscreenUrlPrefix": "", /* 图片访问路径前缀 */
"snapscreenInsertAlign": "none", /* 插入的图片浮动方式 */
/* 抓取远程图片配置 */
"catcherLocalDomain": ["127.0.0.1", "localhost", "img.baidu.com"],
"catcherActionName": "catchimage", /* 执行抓取远程图片的action名称 */
"catcherFieldName": "source", /* 提交的图片列表表单名称 */
"catcherPathFormat": "/upload/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */
"catcherUrlPrefix": "", /* 图片访问路径前缀 */
"catcherMaxSize": 2048000, /* 上传大小限制,单位B */
"catcherAllowFiles": [".png", ".jpg", ".jpeg", ".gif", ".bmp"], /* 抓取图片格式显示 */
/* 上传视频配置 */
"videoActionName": "uploadvideo", /* 执行上传视频的action名称 */
"videoFieldName": "upfile", /* 提交的视频表单名称 */
"videoPathFormat": "/upload/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */
"videoUrlPrefix": "", /* 视频访问路径前缀 */
"videoMaxSize": 102400000, /* 上传大小限制,单位B,默认100MB */
"videoAllowFiles": [
".flv", ".swf", ".mkv", ".avi", ".rm", ".rmvb", ".mpeg", ".mpg",
".ogg", ".ogv", ".mov", ".wmv", ".mp4", ".webm", ".mp3", ".wav", ".mid"], /* 上传视频格式显示 */
/* 上传文件配置 */
"fileActionName": "uploadfile", /* controller里,执行上传视频的action名称 */
"fileFieldName": "upfile", /* 提交的文件表单名称 */
"filePathFormat": "/upload/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */
"fileUrlPrefix": "", /* 文件访问路径前缀 */
"fileMaxSize": 51200000, /* 上传大小限制,单位B,默认50MB */
"fileAllowFiles": [
".png", ".jpg", ".jpeg", ".gif", ".bmp",
".flv", ".swf", ".mkv", ".avi", ".rm", ".rmvb", ".mpeg", ".mpg",
".ogg", ".ogv", ".mov", ".wmv", ".mp4", ".webm", ".mp3", ".wav", ".mid",
".rar", ".zip", ".tar", ".gz", ".7z", ".bz2", ".cab", ".iso",
".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".pdf", ".txt", ".md", ".xml"
], /* 上传文件格式显示 */
/* 列出指定目录下的图片 */
"imageManagerActionName": "listimage", /* 执行图片管理的action名称 */
"imageManagerListPath": "/upload/", /* 指定要列出图片的目录 */
"imageManagerListSize": 20, /* 每次列出文件数量 */
"imageManagerUrlPrefix": "", /* 图片访问路径前缀 */
"imageManagerInsertAlign": "none", /* 插入的图片浮动方式 */
"imageManagerAllowFiles": [".png", ".jpg", ".jpeg", ".gif", ".bmp"], /* 列出的文件类型 */
/* 列出指定目录下的文件 */
"fileManagerActionName": "listfile", /* 执行文件管理的action名称 */
"fileManagerListPath": "/upload/", /* 指定要列出文件的目录 */
"fileManagerUrlPrefix": "", /* 文件访问路径前缀 */
"fileManagerListSize": 20, /* 每次列出文件数量 */
"fileManagerAllowFiles": [
".png", ".jpg", ".jpeg", ".gif", ".bmp",
".flv", ".swf", ".mkv", ".avi", ".rm", ".rmvb", ".mpeg", ".mpg",
".ogg", ".ogv", ".mov", ".wmv", ".mp4", ".webm", ".mp3", ".wav", ".mid",
".rar", ".zip", ".tar", ".gz", ".7z", ".bz2", ".cab", ".iso",
".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".pdf", ".txt", ".md", ".xml"
] /* 列出的文件类型 */
}
完成以上两项配置之后,就可以使用上传图片的功能了。
十、修改公司介绍
点击“首页图片”后面的“浏览”按钮,插入一张图片。

点击编辑器上的插入图片图标,选择一张图片插入。

然后再输入公司简介的内容,点击保存。

进入数据库,可以看到我们刚刚修改后的结果。

十一、设定图片路径
从步骤十的截图可以看到,我们上传的图片显示为一个白色的小图标,而百度的UEditor上传图片之后,是可以显示图片的。
通过测试发现:我们可以通过:http://127.0.0.1:9090/upload/20180312200449/2018031220044969Fw2.jpg 来访问这张上传的图片,但是我们如果用http://127.0.0.1:81/upload/20180312200449/2018031220044969Fw2.jpg 是无法显示这张图片的,而我们现在正在我们的后台,后台的地址就是http://127.0.0.1:81,所以图片当然也无法显示了。
打开nginx配置文件 :E:\Service\nginx-1.11.5\conf\nginx.conf
将location ~* ^/(index|api)/ 修改为 location ~* ^/(index|api|upload)/

然后同时按Ctrl+Alt+Del键,点击启动任务管理器,找到nginx_service.exe,右键=》结束进程树。
重新打开服务(控制面板=》所有控制面板项=》管理工具=》服务),启动nginx_service服务。
刷新一下后台,就可以看到图片正常显示了。

备注:
这一章是最难的一章,两次在这儿栽跟斗,就是源码有问题,我第一次做这个教程的时候做到第十一章,当时是解决了这个问题的。、
不过第二次做时,完全忘记了,所以又犯了同样的错误,就是可以通过info.log查看出错信息,这样就可以发现files.py里面的这段代码是有问题的:
filepath = '/upload/' + datetime_helper.to_number('%Y%m%d') + '/'
to_number是不需要带参数的。