10 发消息和保存图片整合

发消息和保存图片整合

发送接收消息的异步操作 /room

使用 AsyncHTTPClient 和 IOLoop.current().spawn_callback 调用 /async

     client = tornado.httpclient.AsyncHTTPClient()  # 异步客户端
    
     # http://127.0.0.1:8000/async?url=http://source.unsplash.com/random
      save_api_url = "http://127.0.0.1:8000/async?url={}&user={}&from=room".format(url, self.current_user)
    
      # 非阻塞调用内部接口(/async)  client.fetch(http://127.0.0.1:8000/async?url=http://source.unsplash.com/random)
      
      IOLoop.current().spawn_callback(client.fetch, save_api_url)
class ChatSocketHandler(tornado.websocket.WebSocketHandler, SessionMixin):
    
    def on_message(self, message):
       """websocket服务端接收到消息 自动调用此函数"""
    
       print('got message %s' % message)  # got message {"body":"2"}
        
       parsed = tornado.escape.json_decode(message)  # {"body":"2"}
    
       # 用户发送的消息是一个链接
       if parsed['body'] and parsed["body"].startswith("http://"):
        
          url = parsed['body']
            
          client = tornado.httpclient.AsyncHTTPClient()  # 异步客户端
        
          # http://127.0.0.1:8000/async?url=http://source.unsplash.com/random
          save_api_url = "http://127.0.0.1:8000/async?url={}&user={}&from=room".format(url, self.current_user)
        
          # 非阻塞调用内部接口(/async)  client.fetch(http://127.0.0.1:8000/async?url=http://source.unsplash.com/random)
            
          IOLoop.current().spawn_callback(client.fetch, save_api_url)

          chat = make_chat("user[{}],url({}) is processing".format(self.current_user, url))
          chat["html"] = tornado.escape.to_basestring(
             self.render_string("message.html", message=chat))
          self.write_message(chat)  # 消息仅发送给自己
            
       else:
          chat = make_chat("user[{}],say: {}".format(self.current_user, parsed['body']))

          chat["html"] = tornado.escape.to_basestring(
             self.render_string("message.html", message=chat))
        
          ChatSocketHandler.update_cache(chat)  # 更新历史消息列表
            
          ChatSocketHandler.send_updates(chat)  # 向所有在线用户发送消息

增加处理含有图片 URL 的消息 /async

  • 简单的判断是否 是URL

    parsed["body"].startswith("http://")

  • 调用内部接口 /save(非阻塞的调用)

     client = tornado.httpclient.AsyncHTTPClient()  # 异步客户端
    
     # http://127.0.0.1:8000/async?url=http://source.unsplash.com/random
      save_api_url = "http://127.0.0.1:8000/async?url={}&user={}&from=room".format(url, self.current_user)
    
      # 非阻塞调用内部接口(/async)  client.fetch(http://127.0.0.1:8000/async?url=http://source.unsplash.com/random)
      
      IOLoop.current().spawn_callback(client.fetch, save_api_url)

完成图片下载后发送消息

在 /async对应 handler 调用 WSocketHandler.send_updates()

class URLSaveHandler(AuthBaseHandler):
   """保存指定url的图片 同步方法"""

   @tornado.web.authenticated
   def get(self, *args, **kwargs):
      url = self.get_argument('url', None)
      response = self.fetch_image(url)  # 获取指定url的图片
      if not response.body:  # 数据被封装在响应对象的body属性中
         self.write('empty data')
         return

      image_saver = ImageSave(self.settings['static_path'], 'x.jpg')
      image_saver.save_image(response.body)  # body 就是图片数据 保存图片
      image_saver.make_thumbs()  # 做缩略图
      # 添加到数据库,拿到 post 实例
      post = Posts.add_post_for(self.current_user, image_saver.image_url, image_saver.thumb_url)

      print("-- {} -end fetch:#{}".format(datetime.now(), post.id))

      self.redirect('/post/{}'.format(post.id))  # 跳转到 post 页面

   def fetch_image(self, url):
      """获取指定url的图片"""
      client = tornado.httpclient.HTTPClient()  # 获取同步操作对象
      print("-- {} -going to fetch:{}".format(datetime.now(), url))
      response = client.fetch(url)  # 获取url对应的内容  得到响应对象
      return response


class AsyncURLSaveHandler(AuthBaseHandler):
   """保存指定url的图片 异步方法"""

   # @tornado.web.authenticated
   @tornado.gen.coroutine
   def get(self, *args, **kwargs):
      url = self.get_argument('url', None)
      response = yield self.fetch_image(url)  # 获取指定url的图片
      if not response.body:  # 数据被封装在响应对象的body属性中
         self.write('empty data')
         return

      image_saver = ImageSave(self.settings['static_path'], 'x.jpg')
      image_saver.save_image(response.body)  # 保存图片
      image_saver.make_thumbs()  # 缩略图

      user = self.get_argument("user", None)  # user可以在url中获取
      if user:
         post = Posts.add_post_for(user, image_saver.image_url, image_saver.thumb_url)  # 添加到数据库
         print("-- {} -end fetch:#{}".format(datetime.now(), post.id))

         # 注意 展示数据库中的url  传给message.html 的 img的url 是 thumb_url
         chat = make_chat("user[{}] post:127.0.0.1:8000/{}".format(user, post.id), image_saver.thumb_url)
         chat["html"] = tornado.escape.to_basestring(
            self.render_string("message.html", message=chat))
         ChatSocketHandler.update_cache(chat)  # 将抓取到的图片更新到历史消息列表
         ChatSocketHandler.send_updates(chat)  # 向所有在线用户发送图片消息

      else:
         self.write('user error')

      # self.redirect('/post/{}'.format(post.id))

   @tornado.gen.coroutine
   def fetch_image(self, url):
      """获取指定url的图片"""
      client = tornado.httpclient.AsyncHTTPClient()  # 获取异步操作对象
      print("-- {} -going to fetch:{}".format(datetime.now(), url))
      # yield tornado.gen.sleep(6)
      response = yield client.fetch(url)  # 获取url对应的内容  得到响应对象
      return response

访问room http://127.0.0.1:8000/room 填写表单

服务器接收到客户端的消息 判断消息类型

1 若输入的是链接 if parsed['body'] and parsed["body"].startswith("http://"):

​ 异步调用/async接口,通过/async中的异步抓取图片方法fetch_image, 抓取url对应的图片 并保存到数据库post中Posts.add_post_for ,将图片放到历史消息列表中ChatSocketHandler.update_cache(chat) ,再将图片发送给所有在线用户ChatSocketHandler.send_updates

将输入连接的提示内容仅发送在自己的客户端中 self.write_message(chat)

2 若输入的不是链接

​ 将消息放到历史消息列表中ChatSocketHandler.update_cache(chat) ,再将消息发送给所有在线用户ChatSocketHandler.send_updates

要注意的安全问题

  • 内部用户
  • xsrf 保护

作业

使用 /room 接收消息包含的 URL 来保存图片

code

chat.py

import tornado.web
import tornado.websocket
import tornado.httpclient
import tornado.escape
from tornado.ioloop import IOLoop
from pycket.session import SessionMixin
from .main import AuthBaseHandler
import uuid


def make_chat(msg, img_url=None):
   """生成一个用来格式化message.html的dict"""
   chat = {
      "id": str(uuid.uuid4()),
      "body": msg,
      "img_url": img_url  # 注意 展示数据库中的url  传给message.html 的 img的url 是 thumb_url
   }
   return chat


class RoomHandler(AuthBaseHandler):
   """
   聊天室页面
   """

   def get(self, *args, **kwargs):
      self.render('room.html', messages=ChatSocketHandler.cache)


class ChatSocketHandler(tornado.websocket.WebSocketHandler, SessionMixin):
   """
   处理响应websocket连接
   """
   waiters = set()  # 等待接受信息的用户
   cache = []  # 存放历史信息的列表
   cache_size = 200  # 消息列表的大小

   def get_current_user(self):
      return self.session.get('tudo_user_info')

   def open(self, *args, **kwargs):
      """新的websocket连接打开时 自动调用此函数"""
      print('new connection:%s' % self)
      ChatSocketHandler.waiters.add(self)

   def on_close(self):
      """websocketa连接关闭时 自动调用此函数"""
      print('close connection: %s' % self)
      ChatSocketHandler.waiters.remove(self)

   @classmethod
   def update_cache(cls, message):
      """# 更新历史消息列表 加入新的消息"""
      cls.cache.append(message)
      if len(cls.cache) > cls.cache_size:
         cls.cache = cls.cache[-cls.cache_size:]

   @classmethod
   def send_updates(cls, chat):
      """# 向所有在线用户发送消息"""
      for waiter in cls.waiters:
         waiter.write_message(chat)

   def on_message(self, message):
      """websocket服务端接收到消息 自动调用此函数"""
      print('got message %s' % message)  # got message {"body":"2"}
      parsed = tornado.escape.json_decode(message)  # {"body":"2"}
      # 用户发送的消息是一个链接
      if parsed['body'] and parsed["body"].startswith("http://"):
         url = parsed['body']
         client = tornado.httpclient.AsyncHTTPClient()  # 异步客户端
         # http://127.0.0.1:8000/async?url=http://source.unsplash.com/random
         save_api_url = "http://127.0.0.1:8000/async?url={}&user={}&from=room".format(url, self.current_user)
         # 非阻塞调用内部接口(/async)  client.fetch(http://127.0.0.1:8000/async?url=http://source.unsplash.com/random)
         IOLoop.current().spawn_callback(client.fetch, save_api_url)

         chat = make_chat("user[{}],url({}) is processing".format(self.current_user, url))
         chat["html"] = tornado.escape.to_basestring(
            self.render_string("message.html", message=chat))
         self.write_message(chat)  # 消息仅发送给自己
      else:
         chat = make_chat("user[{}],say: {}".format(self.current_user, parsed['body']))

         chat["html"] = tornado.escape.to_basestring(
            self.render_string("message.html", message=chat))
         ChatSocketHandler.update_cache(chat)  # 更新历史消息列表
         ChatSocketHandler.send_updates(chat)  # 向所有在线用户发送消息

service.py

from datetime import datetime
import time
import uuid
import tornado.web
import tornado.httpclient
import tornado.gen
import tornado.escape

from .main import AuthBaseHandler
from utils.photo import ImageSave
from models.account import Posts
from .chat import ChatSocketHandler, make_chat


class URLSaveHandler(AuthBaseHandler):
   """保存指定url的图片 同步方法"""

   @tornado.web.authenticated
   def get(self, *args, **kwargs):
      url = self.get_argument('url', None)
      response = self.fetch_image(url)  # 获取指定url的图片
      if not response.body:  # 数据被封装在响应对象的body属性中
         self.write('empty data')
         return

      image_saver = ImageSave(self.settings['static_path'], 'x.jpg')
      image_saver.save_image(response.body)  # body 就是图片数据 保存图片
      image_saver.make_thumbs()  # 做缩略图
      # 添加到数据库,拿到 post 实例
      post = Posts.add_post_for(self.current_user, image_saver.image_url, image_saver.thumb_url)

      print("-- {} -end fetch:#{}".format(datetime.now(), post.id))

      self.redirect('/post/{}'.format(post.id))  # 跳转到 post 页面

   def fetch_image(self, url):
      """获取指定url的图片"""
      client = tornado.httpclient.HTTPClient()  # 获取同步操作对象
      print("-- {} -going to fetch:{}".format(datetime.now(), url))
      response = client.fetch(url)  # 获取url对应的内容  得到响应对象
      return response


class AsyncURLSaveHandler(AuthBaseHandler):
   """保存指定url的图片 异步方法"""

   # @tornado.web.authenticated
   @tornado.gen.coroutine
   def get(self, *args, **kwargs):
      url = self.get_argument('url', None)
      response = yield self.fetch_image(url)  # 获取指定url的图片
      if not response.body:  # 数据被封装在响应对象的body属性中
         self.write('empty data')
         return

      image_saver = ImageSave(self.settings['static_path'], 'x.jpg')
      image_saver.save_image(response.body)  # 保存图片
      image_saver.make_thumbs()  # 缩略图

      user = self.get_argument("user", None)  # user可以在url中获取
      if user:
         post = Posts.add_post_for(user, image_saver.image_url, image_saver.thumb_url)  # 添加到数据库
         print("-- {} -end fetch:#{}".format(datetime.now(), post.id))

         # 注意 展示数据库中的url  传给message.html 的 img的url 是 thumb_url
         chat = make_chat("user[{}] post:127.0.0.1:8000/{}".format(user, post.id), image_saver.thumb_url)
         chat["html"] = tornado.escape.to_basestring(
            self.render_string("message.html", message=chat))
         ChatSocketHandler.update_cache(chat)  # 将抓取到的图片更新到历史消息列表
         ChatSocketHandler.send_updates(chat)  # 向所有在线用户发送图片消息

      else:
         self.write('user error')

      # self.redirect('/post/{}'.format(post.id))

   @tornado.gen.coroutine
   def fetch_image(self, url):
      """获取指定url的图片"""
      client = tornado.httpclient.AsyncHTTPClient()  # 获取异步操作对象
      print("-- {} -going to fetch:{}".format(datetime.now(), url))
      # yield tornado.gen.sleep(6)
      response = yield client.fetch(url)  # 获取url对应的内容  得到响应对象
      return response

message.html

<div class="message" id="m{{ message["id"] }}">
    {% module linkify(message["body"]) %}
    <br>
    {% if message["img_url"] %}
        <img src="{{ static_url(message["img_url"]) }}">
    {% end %}
</div>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值