Tornado实战-图片上传和图片展示

项目主要部分

  • 发现或最近上传的图片页面
  • 所关注的用户图片流
  • 单个图片详情页面
  • 数据库 Database
  • 用户档案 User Profile

如何去做

  • 从最简单开始,迭代增加功能
  • 用户,登陆,关注等
  • 数据库保存
  • UI 和 Web 界面美化
  • 外部连接
  • 部署和运行

基本页面

  • 发现或最近上传的图片页面 /explore ExploreHandler
  • 所关注的用户图片流 / IndexHandler
  • 单个图片详情页面 /post/id PostHandler

代码

image.png

app.py

import tornado.ioloop   #开启循环,让服务一直等待请求的到来
import tornado.web      #web服务基本功能都封装在此模块中
import tornado.options  #从命令行中读取设置
from tornado.options import define,options  #导入包

from handlers import main

define('port',default='8000',help='Listening port',type=int) #定义如何接受传进来的东西


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), #命名组写法,使用关键字,路由与handler方法不一定顺序一致
            (r'/upload',main.UploadHandler)
        ]
        settings = dict(
            debug = True, #调试模式,修改后自动重启服务,不需要自动重启,生产情况下切勿开启,安全性
            template_path = 'templates', #模板文件目录,想要Tornado能够正确的找到html文件,需要在 Application 中指定文件的位置
            static_path = 'static'  #静态文件目录,可用于用于访问js,css,图片之类的添加此配置之后,tornado就能自己找到静态文件
        )

        super(Application,self).__init__(handlers,**settings) #用super方法将父类的init方法重新执行一遍,然后将handlers和settings传进去,完成初始化


app = Application() #实例化



if __name__ == '__main__':   #当.py文件被直接运行时,代码块将被运行;当.py文件以模块形式被导入时,代码块不被运行。

    tornado.options.parse_command_line()
    app.listen(options.port)  ##如果一个与define语句中同名的设置在命令行中被给出,那么它将成为全局的options的一个属性 即 options.port 相当于define的url的port
    print("Server start on port {}".format(str(options.port)))  #提示服务启动占用端口
    tornado.ioloop.IOLoop.current().start()   #执行ioloop

main.py

import tornado.web
import os
from utils import photo

class IndexHandler(tornado.web.RequestHandler):
    """
     Home page for user,photo feeds 主页----所关注的用户图片流
    """
    def get(self,*args,**kwargs):
        self.render('index.html') #打开index.html网页


class ExploreHandler(tornado.web.RequestHandler):
    """
    Explore page,photo of other users 发现页-----发现或最近上传的图片页面
    """
    def get(self,*args,**kwargs):
        # image_urls = get_images("./static/uploads")  #打开指定路径下的文件,或者static/uploads
        os.chdir('static')  # 用于改变当前工作目录到指定的路径
        image_urls = photo.get_images("uploads/thumbs")
        os.chdir("..")
        self.render('explore.html',image_urls=image_urls)

class PostHandler(tornado.web.RequestHandler):
    """
    Single photo page and maybe  单个图片详情页面
    """
    def get(self,post_id):
        print(post_id)
        self.render('post.html',post_id = post_id)   #根据正则输入的内容,接收到,打开相应的图片


class UploadHandler(tornado.web.RequestHandler):  #上传文件
    def get(self,*args,**kwargs):
        self.render('upload.html')

    def post(self,*args,**kwargs):
        file_imgs = self.request.files.get('newImg',None)  #获取上传文件数据,返回文件列表

        for file_img in file_imgs: #可能同一个上传的文件会有多个文件,所以要用for循环去迭代它
            # filename 文件的实际名字,body 文件的数据实体;content_type 文件的类型。 这三个对象属性可以像字典一样支持关键字索引
            save_to = 'static/uploads/{}'.format(file_img['filename'])
            #以二进制格式打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。一般用于非文本文件如图片等。
            with open(save_to,'wb') as f: #二进制
                f.write(file_img['body'])
            photo.make_thumb(save_to) #同时生成缩略图

        self.redirect('/explore')

photo.py

import glob,os
from PIL import Image

def get_images(path): #将某个路径下所有的文件会返回成一个列表,glob支持*?[]这三种通配符

    #glob模块的主要方法就是glob,该方法返回所有匹配的文件路径列表(list)
    image_urls = glob.glob(path + '/*.jpg') #所有path目录下面为*.jpg的图片路径
    return image_urls

#生成缩略图
def make_thumb(path):
    im = Image.open(path)  #打开图片
    im.thumbnail((200,200)) #thumbnail函数接受一个元组作为参数,分别对应着缩略图的宽高,在缩略时,函数会保持图片的宽高比例。
    name = os.path.basename(path) #返回path最后的文件名。如何path以/或\结尾,那么就会返回空值。
    filename,ext = os.path.splitext(name)  #分离文件名与扩展名;默认返回(fname,fextension)元组,可做分片操作
    #save to filename_200x200.jpg
    im.save('static/uploads/thumbs/{}_{}x{}{}'.format(filename,200,200,ext))

base.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title%}Tornado Title{% end %}</title>
</head>
<body>
{% block content %}base content{% end %}
</body>
</html>

index.html

{% extends 'base.html' %} #继承base.html

{% block title %} index page {% end %}

{% block content %}index content
{% for num in range(1,5) %}
<a href="/post/{{num}}">
    <img src="{{static_url('images/{}.jpg'.format(num))}}" /> <!-- 使用此方法时,Tornado 会自动地给静态文件添加版本号,如果版本号更改了,浏览器会自动的缓存新的静态文件-->
</a>
{% end %}
{% end %}

upload.html

{% extends 'base.html' %}

{% block title %} uploads page{% end %}

{% block content %} content page

<!--如果表单需要上传文件,需要设置enctype="multipart/form-data" 否则文件无法找到 -->
<form action="/upload" enctype="multipart/form-data" method="post">
    <input type="file" name="newImg">
    <input type="submit">
</form>
{% end %}

explore.html

{% extends 'base.html' %}

{% block title %} explore page {% end %}

{% block content %} explore content

{% for url in image_urls %}

<!--<img src="{{url}}" />-->
<!--<img src="{{static_url(url)}}" width="200"/>-->
<img src="{{static_url(url)}}"/>

{% end %}

{% end %}

post.html

{% extends 'base.html' %}

{% block title %} post page {% end %}

{% block content %}
post of {{post_id}} content

<img src="{{static_url('images/{}.jpg'.format(post_id))}}" width="300"/>

{% end %} #获取post_id并write到网页

页面效果图

image.png

image.png

image.png

要点

上传表单和文件保存

  • self.request.files 的使用
  • Python 操作文件,写入数据

用简单的目录检索来展示

使用 Python 标准库 glob

缩略图生成

pip install pillow
使用 PIL

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,657评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,889评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,057评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,509评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,562评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,443评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,251评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,129评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,561评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,779评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,902评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,621评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,220评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,838评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,971评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,025评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,843评论 2 354

推荐阅读更多精彩内容