需要模塊
pip install redis
pip install packet
實(shí)現(xiàn)代碼
app.py
import tornado.ioloop #開啟循環(huán),讓服務(wù)一直等待請(qǐng)求的到來(lái)
import tornado.web #web服務(wù)基本功能都封裝在此模塊中
import tornado.options #從命令行中讀取設(shè)置
from tornado.options import define,options #導(dǎo)入包
from handlers import main,auth
define('port',default='8000',help='Listening port',type=int) #定義如何接受傳進(jìn)來(lái)的東西
class Application(tornado.web.Application): #引入Application類,重寫方法示弓,這樣做的好處在于可以自定義块差,添加另一些功能
def __init__(self):
handlers = [
(r'/',main.IndexHandler),
(r'/explore',main.ExploreHandler),
(r'/post/(?P<post_id>[0-9]+)',main.PostHandler), #命名組寫法,使用關(guān)鍵字贡茅,路由與handler方法不一定順序一致
(r'/upload',main.UploadHandler),
(r'/login',auth.LoginHandler),
(r'/logout',auth.LogoutHandler),
(r'/register',auth.RegisterHandler)
]
settings = dict(
debug = True, #調(diào)試模式,修改后自動(dòng)重啟服務(wù)状答,不需要自動(dòng)重啟粉洼,生產(chǎn)情況下切勿開啟,安全性
template_path='templates', #模板文件目錄,想要Tornado能夠正確的找到html文件仅财,需要在 Application 中指定文件的位置
static_path='static', #靜態(tài)文件目錄,可用于用于訪問(wèn)js,css,圖片之類的添加此配置之后狈究,tornado就能自己找到靜態(tài)文件
login_url='/login', #沒(méi)有登錄則跳轉(zhuǎn)至此
cookie_secret='1q2w3e4r', # 加密cookie的字符串
pycket={ #固定寫法packet,用于保存用戶登錄信息
'engine': 'redis',
'storage': {
'host': 'localhost',
'port': 6379,
'db_sessions': 5,
'db_notifications': 11,
'max_connections': 2 ** 33,
},
'cookie': {
'expires_days': 38,
'max_age': 100
}
}
)
super(Application,self).__init__(handlers,**settings) #用super方法將父類的init方法重新執(zhí)行一遍盏求,然后將handlers和settings傳進(jìn)去抖锥,完成初始化
app = Application() #實(shí)例化
if __name__ == '__main__': #當(dāng).py文件被直接運(yùn)行時(shí),代碼塊將被運(yùn)行碎罚;當(dāng).py文件以模塊形式被導(dǎo)入時(shí)磅废,代碼塊不被運(yùn)行。
tornado.options.parse_command_line()
app.listen(options.port) ##如果一個(gè)與define語(yǔ)句中同名的設(shè)置在命令行中被給出荆烈,那么它將成為全局的options的一個(gè)屬性 即 options.port 相當(dāng)于define的url的port
print("Server start on port {}".format(str(options.port))) #提示服務(wù)啟動(dòng)占用端口
tornado.ioloop.IOLoop.current().start() #執(zhí)行ioloop
main.py
import tornado.web
import os
from pycket.session import SessionMixin
from utils import photo
class AuthBaseHandler(tornado.web.RequestHandler,SessionMixin):
def get_current_user(self): #重寫get_current_user()方法
return self.session.get('user_info',None) #session是一種會(huì)話狀態(tài)拯勉,跟數(shù)據(jù)庫(kù)的session可能不一樣
#添加裝飾器,裝飾需要驗(yàn)證的請(qǐng)求
class IndexHandler(AuthBaseHandler):
"""
Home page for user,photo feeds 主頁(yè)----所關(guān)注的用戶圖片流
"""
@tornado.web.authenticated #@tornado.web.authenticated裝飾器包裹get方法時(shí),表示這個(gè)方法只有在用戶合法時(shí)才會(huì)調(diào)用憔购,authenticated裝飾器會(huì)調(diào)用get_current_user()方法獲取current_user的值宫峦,若值為False,則重定向到登錄url裝飾器判斷有沒(méi)有登錄玫鸟,如果沒(méi)有則跳轉(zhuǎn)到配置的路由下去导绷,但是要在app.py里面設(shè)置login_url
def get(self,*args,**kwargs):
self.render('index.html')
class ExploreHandler(AuthBaseHandler):
"""
Explore page,photo of other users 發(fā)現(xiàn)頁(yè)-----發(fā)現(xiàn)或最近上傳的圖片頁(yè)面
"""
@tornado.web.authenticated
def get(self,*args,**kwargs):
# image_urls = get_images("./static/uploads") #打開指定路徑下的文件,或者static/uploads
os.chdir('static') # 用于改變當(dāng)前工作目錄到指定的路徑
image_urls = photo.get_images("uploads/thumbs")
os.chdir("..")
self.render('explore.html',image_urls=image_urls)
class PostHandler(AuthBaseHandler):
"""
Single photo page and maybe 單個(gè)圖片詳情頁(yè)面
"""
@tornado.web.authenticated
def get(self,post_id):
print(post_id)
self.render('post.html',post_id = post_id) #根據(jù)正則輸入的內(nèi)容屎飘,接收到妥曲,打開相應(yīng)的圖片
class UploadHandler(AuthBaseHandler): #上傳文件
@tornado.web.authenticated
def get(self,*args,**kwargs):
self.render('upload.html')
def post(self,*args,**kwargs):
file_imgs = self.request.files.get('newImg',None) #獲取上傳文件數(shù)據(jù),返回文件列表
for file_img in file_imgs: #可能同一個(gè)上傳的文件會(huì)有多個(gè)文件枚碗,所以要用for循環(huán)去迭代它
# filename 文件的實(shí)際名字逾一,body 文件的數(shù)據(jù)實(shí)體;content_type 文件的類型肮雨。 這三個(gè)對(duì)象屬性可以像字典一樣支持關(guān)鍵字索引
save_to = 'static/uploads/{}'.format(file_img['filename'])
#以二進(jìn)制格式打開一個(gè)文件只用于寫入。如果該文件已存在則打開文件箱玷,并從開頭開始編輯怨规,即原有內(nèi)容會(huì)被刪除。如果該文件不存在锡足,創(chuàng)建新文件波丰。一般用于非文本文件如圖片等。
with open(save_to,'wb') as f: #二進(jìn)制
f.write(file_img['body'])
photo.make_thumb(save_to) #同時(shí)生成縮略圖
self.redirect('/explore')
auth.py
import tornado.web
from utils.account import authenticate
from .main import AuthBaseHandler
class RegisterHandler(AuthBaseHandler):
def get(self, *args, **kwargs):
print('register')
self.render('register.html')
def post(self, *args, **kwargs):
print('registerpost')
username = self.get_argument('username','')
password1 = self.get_argument('password1','')
password2 = self.get_argument('password2','')
if username and password1 and (password1 == password2):
pass
else:
self.write({'msg':'register fail'})
class LoginHandler(AuthBaseHandler):
def get(self,*args,**kwargs):
if self.current_user: #若用戶已登錄
self.redirect('/') #那么直接跳轉(zhuǎn)到主頁(yè)
else:
nextname = self.get_argument('next','') #將原來(lái)的路由賦值給nextname
self.render('login.html',nextname = nextname) #否則去登錄界面
def post(self,*args,**kwargs):
username = self.get_argument('username',None)
password = self.get_argument('password',None)
passed = authenticate(username,password)
if passed:
self.session.set('user_info',username) #將前面設(shè)置的cookie設(shè)置為username舶得,保存用戶登錄信息
next_url = self.get_argument('next', '') # 獲取之前頁(yè)面的路由
if next_url:
self.redirect(next_url) #跳轉(zhuǎn)主頁(yè)路由
else:
self.redirect('/')
else:
self.write({'msg':'login fail'}) #不通過(guò)掰烟,有問(wèn)題
class LogoutHandler(AuthBaseHandler):
def get(self, *args, **kwargs):
#self.session.set('user_info','') #將用戶的cookie清除
self.session.delete('user_info')
self.redirect('/login')
db.py
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
HOSTNAME = '127.0.0.1'
PORT = '3306' #注意這個(gè)不是本地端口是指遠(yuǎn)程數(shù)據(jù)庫(kù)端口,因?yàn)閜ycharm已經(jīng)先SSH連接到本地了
DATABASE = 'my_db'
USERNAME = 'root'
PASSWORD = '1q2w3e4r'
db_url = 'mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8'.format(
USERNAME,
PASSWORD,
HOSTNAME,
PORT,
DATABASE
)
#連接數(shù)據(jù)庫(kù)
engine = create_engine(db_url)
Base = declarative_base(bind = engine) #這個(gè)基類是維系類和數(shù)據(jù)表關(guān)系的目錄。
#在對(duì)表數(shù)據(jù)進(jìn)行增刪改查之前纫骑,先需要建立會(huì)話蝎亚,建立會(huì)話之后才能進(jìn)行操作,就類似于文件要打開之后才能對(duì)文件內(nèi)容操作先馆。
Session = sessionmaker(engine)
session = Session()
users.py
from datetime import datetime
from sqlalchemy import Column,Integer,String,DateTime
from .db import Base
class User(Base):
__tablename__ = 'users'
id = Column(Integer,primary_key=True,autoincrement=True)
name = Column(String(50),unique=True,nullable=False)
password = Column(String(50),nullable=False)
last_login = Column(DateTime,default=datetime.now)
def __repr__(self):
return '<User #{}:{}>'.format(self.id,self.name)
account.py
import hashlib
def hash(text):
text = hashlib.md5(text.encode()).hexdigest() #給密碼加密发框,用hashlib來(lái)算法加密,utf8不加的話就是默認(rèn)utf8
return text
USER_DATA = {
'name':'user',
'password':hash('1q2w3e4r')
}
def authenticate(username,password):#用戶密碼匹配判斷函數(shù)
if username and password:
hash_pwd = hash(password)
if username == USER_DATA['name'] and hash_pwd == USER_DATA['password']: #是否與保存的一致
return True
return False
index.html
{% extends 'base.html' %} #繼承base.html
{% block title %} index page {% end %}
{% block content %}
index content
current_user:{{current_user}} <!--獲取當(dāng)前用戶 -->
{% for num in range(1,5) %}
<a href="/post/{{num}}">
<img src="{{static_url('images/{}.jpg'.format(num))}}" /> <!-- 使用此方法時(shí)煤墙,Tornado 會(huì)自動(dòng)地給靜態(tài)文件添加版本號(hào)梅惯,如果版本號(hào)更改了,瀏覽器會(huì)自動(dòng)的緩存新的靜態(tài)文件-->
</a>
{% end %}
{% end %}
image.png
