5 图片上传和展示完善

回顾上节内容

  • 使用数据库的登录和注册
  • 使用数据库保图片 url 和展示

改进图片上传

路径处理

对保存的路径和缩略图路径,URL 的处理

photo.py
class ImageSave(object):
   """
   保存图片
   """
   upload_dir = 'uploads'
   thumb_dir = 'thumbs'
   size = (200, 200)

   def __init__(self, static_path, name):
      self.static_path = static_path  # static
      self.oldname = name  # 701728.jpg
      self.newname = self.gen_new_name()

   def gen_new_name(self):
      """
      生成随机唯一的字符串(文件名)
      :return:
      """
      _, ext = os.path.splitext(self.oldname)
      return uuid.uuid4().hex + ext

   # 图片的url: uploads / 1202983.jpg
   @property
   def image_url(self):
      return os.path.join(self.upload_dir, self.newname)

   # 图片上传后的保存路径 static / uploads / 1202983.jpg
   @property
   def upload_path(self):
      return os.path.join(self.static_path, self.image_url)

   # 缩略图的url:uploads / thumbs / 1202983_200x200.jpg
   @property
   def thumb_url(self):
      filename, ext = os.path.splitext(self.newname)
      thumb_name = '{}_{}x{}{}'.format(filename, *self.size, ext)
      return os.path.join(self.upload_dir, self.thumb_dir, thumb_name)

   # 保存图片
   def save_image(self, content):
      with open(self.upload_path, 'wb') as f:
         f.write(content)

   # 生成缩略图
   def make_thumbs(self):
      im = Image.open(self.upload_path)
      im.thumbnail(self.size)
      # static / uploads / thumbs / 1202983.jpg
      save_thumb_to_ = os.path.join(self.static_path, self.thumb_url)
      im.save(save_thumb_to_, 'JPEG')
main.py
class UploadHandler(AuthBaseHandler):
   """
      接受图片上传
      """

   @tornado.web.authenticated
   def get(self, *args, **kwargs):
      self.render('upload.html')

   def post(self, *args, **kwargs):
      # 提取表单中‘name’为‘newimg’的文件元数据   获取上传文件信息
      img_files = self.request.files.get('newimg')

      if img_files:
         post_id = 0
         for img in img_files:
            # 保存的图片的目录 名字
            image_saver = ImageSave(self.settings['static_path'], img['filename'])
            # 保存图片
            image_saver.save_image(img['body'])

            # 生成缩略图  ./ static / uploads /thumbs/ 701728_200x200.jpg
            image_saver.make_thumbs()

            # 保存图片的用户 大图地址 缩略图地址  把url存到数据库
            post = Posts.add_post_for(self.current_user, image_saver.image_url, image_saver.thumb_url)
            post_id = post.id

         self.redirect('post/{}'.format(post_id))
      else:
         self.write({'msg': 'empty form data'})

上传图片名字唯一

用 uuid 库生成

uuid.uuid4().hex 



In [2]: uuid.uuid4()
Out[2]: UUID('c27cbc55-c544-4375-a0d8-ed7f50cfc2aa')

In [3]: uuid.uuid4().hex
Out[3]: '17d9d7327d624edf99117f7f2c43d596'

改进首页 /

显示用户自己上传的图片,可以点击查看具体图片页

main.py

class IndexHandler(AuthBaseHandler):
   """
   首页
   """

   @tornado.web.authenticated
   def get(self, *args, **kwargs):
      post_list = get_post_for(self.current_user)
      self.render('index.html', post_list=post_list)

utils/account.py

def get_post_for(username):
   """
   获取用户上传的图片信息
   :param username:
   :return:
   """
   user = session.query(Users).filter_by(name=username).first()
   if user:
      return user.posts
   else:
      return []

index.html

{% extends 'base.html' %}

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

{% block content %}
    <p><a href="/logout">登出</a></p>
    {% for post in post_list %}
        <a href="post/{{ post.id }}">
            <img src="{{ static_url(post.image_url) }}" width="666">
        </a>
    {% end %}

{% end %}

改进发现页 /explore

显示所有图片缩略图视图,可以点击查看具体图片页

main.py

class ExploreHandler(AuthBaseHandler):
   """
   发现页
   """

   @tornado.web.authenticated
   def get(self, *args, **kwargs):
      post_list = get_post_all()
      self.render('explore.html', post_list=post_list)

utils/account.py

def get_post_all():
   """
   获取所有图片信息(降序)
   :return:
   """
   post_list = session.query(Posts).order_by(Posts.id.desc()).all()
   return post_list

explore.html

{% extends 'base.html' %}

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

{% block content %}
      <p><a href="/logout">登出</a></p>
     {% for post in post_list %}
        <a href="post/{{ post.id }}">
            <img src="{{ static_url(post.thumb_url) }}">
        </a>
    {% end %}
{% end %}

改进单独页 /post/2

显示具体图片页面

main.py

class PostHandler(AuthBaseHandler):
   """
   详情页
   """

   @tornado.web.authenticated
   def get(self, post_id):
      post = get_post(post_id)
      if not post:
         self.write('post id is not exists')
      else:
         self.render('post.html', post=post)

utils/account.py

def get_post(post_id):
   """
   获取用户的特定图片
   :param post_id:
   :return:
   """
   post = session.query(Posts).filter_by(id=post_id).first()
   return post

post.html

{% extends 'base.html' %}

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

{% block content %}
    <img src="{{ static_url(post.image_url)}}" width="560">
    upload by {{ post.user.name }}
{% end %}

作业

进行图片上传的改进和完善3个页面

完整代码

main.py

import tornado.web
from utils import photo
from models.account import Posts
from pycket.session import SessionMixin
from utils.account import get_post_for, get_post, get_post_all
from utils.photo import ImageSave
import os


class AuthBaseHandler(tornado.web.RequestHandler, SessionMixin):
   def get_current_user(self):
      return self.session.get('tudo_user_info')


class IndexHandler(AuthBaseHandler):
   """
   首页
   """

   @tornado.web.authenticated
   def get(self, *args, **kwargs):
      post_list = get_post_for(self.current_user)
      self.render('index.html', post_list=post_list)


class ExploreHandler(AuthBaseHandler):
   """
   发现页
   """

   @tornado.web.authenticated
   def get(self, *args, **kwargs):
      post_list = get_post_all()
      self.render('explore.html', post_list=post_list, current_user=self.current_user)


class PostHandler(AuthBaseHandler):
   """
   详情页
   """

   @tornado.web.authenticated
   def get(self, post_id):
      post = get_post(post_id)
      if not post:
         self.write('post id is not exists')
      else:
         self.render('post.html', post=post)


class UploadHandler(AuthBaseHandler):
   """
      接受图片上传
      """

   @tornado.web.authenticated
   def get(self, *args, **kwargs):
      self.render('upload.html')

   def post(self, *args, **kwargs):
      # 提取表单中‘name’为‘newimg’的文件元数据   获取上传文件信息
      img_files = self.request.files.get('newimg')

      if img_files:
         post_id = 0
         for img in img_files:
            # 保存的图片的目录 名字
            image_saver = ImageSave(self.settings['static_path'], img['filename'])
            # 保存图片
            image_saver.save_image(img['body'])

            # 生成缩略图  ./ static / uploads /thumbs/ 701728_200x200.jpg
            image_saver.make_thumbs()

            # 保存图片的用户 大图地址 缩略图地址  把url存到数据库
            post = Posts.add_post_for(self.current_user, image_saver.image_url, image_saver.thumb_url)
            post_id = post.id

         self.redirect('post/{}'.format(post_id))
      else:
         self.write({'msg': 'empty form data'})

utils/account.py

import hashlib
from models.account import Users, session, Posts
from datetime import datetime


def hashed(passwd):
   return hashlib.md5(passwd.encode('utf8')).hexdigest()


def authenticate(username, password):
   """
   登录认证
   :param username:
   :param password:
   :return:
   """
   if username and password:
      # 获取数据库中username对应的密码
      user_passwd = Users.get_passwd(username)
      # 如果用户存在 密码匹配
      if user_passwd and user_passwd == hashed(password):
         return True
   return False


def register(username, password, email):
   """
   注册
   :param username:
   :param password:
   :param email:
   :return:
   """
   # 查看用户是否存在于数据库中
   if Users.is_exists(username):
      return {'msg': '用户已存在'}
   hash_passwd = hashed(password)
   # 添加用户到数据库
   Users.add_user(username, hash_passwd, email)
   return {'msg': 'ok'}


def save_last_login(username):
   """
   # 保存用户username最后登录时间
   :param username:
   :return:
   """
   t = datetime.now()
   print("user {} login at {}".format(username, t))
   session.query(Users).filter_by(name=username).update({Users.last_login: t})
   session.commit()


def get_post_for(username):
   """
   获取用户上传的图片信息
   :param username:
   :return:
   """
   user = session.query(Users).filter_by(name=username).first()
   if user:
      return user.posts
   else:
      return []


def get_post_all():
   """
   获取所有图片信息(降序)
   :return:
   """
   post_list = session.query(Posts).order_by(Posts.id.desc()).all()
   return post_list


def get_post(post_id):
   """
   获取用户的特定图片
   :param post_id:
   :return:
   """
   post = session.query(Posts).filter_by(id=post_id).first()
   return post

photo.py

import os
import glob
from PIL import Image
import uuid


class ImageSave(object):
   """
   保存图片
   """
   upload_dir = 'uploads'
   thumb_dir = 'thumbs'
   size = (200, 200)

   def __init__(self, static_path, name):
      self.static_path = static_path  # static
      self.oldname = name  # 701728.jpg
      self.newname = self.gen_new_name()

   def gen_new_name(self):
      """
      生成随机唯一的字符串(文件名)
      :return:
      """
      _, ext = os.path.splitext(self.oldname)
      return uuid.uuid4().hex + ext

   # 图片的url: uploads / 1202983.jpg
   @property
   def image_url(self):
      return os.path.join(self.upload_dir, self.newname)

   # 图片上传后的保存路径 static / uploads / 1202983.jpg
   @property
   def upload_path(self):
      return os.path.join(self.static_path, self.image_url)

   # 缩略图的url:uploads / thumbs / 1202983_200x200.jpg
   @property
   def thumb_url(self):
      filename, ext = os.path.splitext(self.newname)
      thumb_name = '{}_{}x{}{}'.format(filename, *self.size, ext)
      return os.path.join(self.upload_dir, self.thumb_dir, thumb_name)

   # 保存图片
   def save_image(self, content):
      with open(self.upload_path, 'wb') as f:
         f.write(content)

   # 生成缩略图
   def make_thumbs(self):
      im = Image.open(self.upload_path)
      im.thumbnail(self.size)
      # static / uploads / thumbs / 1202983.jpg
      save_thumb_to_ = os.path.join(self.static_path, self.thumb_url)
      im.save(save_thumb_to_, 'JPEG')

index.html

{% extends 'base.html' %}

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

{% block content %}
    <p><a href="/logout">登出</a></p>
    {% for post in post_list %}
        <a href="post/{{ post.id }}">
            <img src="{{ static_url(post.image_url) }}" width="666">
        </a>
    {% end %}

{% end %}

explore.html

{% extends 'base.html' %}

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

{% block content %}
      <p><a href="/logout">登出</a></p>
     {% for post in post_list %}
        <a href="post/{{ post.id }}">
            <img src="{{ static_url(post.thumb_url) }}">
        </a>
    {% end %}
{% end %}

post.html

{% extends 'base.html' %}

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

{% block content %}
    <img src="{{ static_url(post.image_url)}}" width="560">
    upload by {{ post.user.name }}
{% end %}

base.html

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

login.html

{% extends 'base.html' %}

{% block title %}
login page
{% end %}

{% block content %}

    <div class="">
        {% if error %}
            {{ error }}
        {% end %}
    <form action="/login?next={{ nextname }}" method="post" enctype="multipart/form-data">
        <div class="form-group">
            Username
            <input autofocus="" class="form-control" id="id_username" maxlength="254" name="username" type="text"
                   required="">
        </div>

        <div class="form-group">
            Password
            <input class="form-control" id="id_password" name="password" type="password" required="">
        </div>

        <button class="">Login</button>

        <div>
            还没有账号 需要<a href="/signup">注册</a>一个
        </div>
    </form>
</div>
{% end %}

signup.html

{% extends 'base.html' %}

{% block title %}
     signup page
{% end %}

{% block content %}

    <div class="">
        {% if msg %}
            {{ msg }}
        {% end %}
    </div>

    <form action="/signup" enctype="multipart/form-data" method="post">
        <div class="form-group">
            Username
            <input autofocus="" class="form-control" id="id_username" maxlength="150" name="username" type="text" required="">
        </div>
        <div class="form-group">
            Email
            <input class="form-control" id="id_email" name="email" type="email" required="">
        </div>
        <div class="form-group">
            Password
            <input class="form-control" id="id_password1" name="password1" type="password" required="">
        </div>
        <div class="form-group">
            Password confirmation
            <input class="form-control" id="id_password2" name="password2" type="password" required="">
        </div>
        <button class="btn btn-default">注册</button>
        <div class="text-center help-text">
            已有账号请 <a href="/login">登录</a>
        </div>
    </form>
{% end %}

upload.html

{% extends base.html %}

{% block title %}
    upload
{% end %}

{% block content %}
    <p><a href="/logout">登出</a></p>
    <form action="/upload" method="post" enctype="multipart/form-data">
        <input type="file" name="newimg">
        <input type="submit">
    </form>
{% end %}

app.py

import tornado.web
import tornado.options
import tornado.ioloop
from tornado.options import define, options

from handlers import main,auth

define(name='port', default='8000', type=int, help='run port')


class Application(tornado.web.Application):
   def __init__(self):
      handlers = [
         (r'/', main.IndexHandler),
         (r'/explore', main.ExploreHandler),
         (r'/post/(?P<post_id>[0-9]+)', main.PostHandler),
         (r'/upload', main.UploadHandler),
         (r'/login', auth.LoginHandler),
         (r'/logout', auth.LogoutHandler),
         (r'/signup', auth.SignupHandler),
      ]
      settings = dict(
         debug=True,
         template_path='templates',
         static_path='static',
         login_url='/login',
         cookie_secret='bZJc2sWbQLKos6GkHn/VB9oXwQt8S0R0kRvJ5/xJ89E=',
         pycket={
            'engine': 'redis',
            'storage': {
               'host': 'localhost',
               'port': 6379,
               # 'password': '',
               'db_sessions': 5,  # redis db index
               'db_notifications': 11,
               'max_connections': 2 ** 30,
            },
            'cookies': {
               'expires_days': 30,
            },
         }
      )

      super(Application, self).__init__(handlers, **settings)


application = Application()

if __name__ == '__main__':
   tornado.options.parse_command_line()
   application.listen(options.port)
   print("Server start on port {}".format(str(options.port)))
   tornado.ioloop.IOLoop.current().start()

models/account.py

from sqlalchemy import (Column, Integer, String, DateTime, ForeignKey)
from sqlalchemy import exists
from sqlalchemy.orm import relationship
from sqlalchemy.sql import exists

from .db import Base, DBSession

from datetime import datetime

session = DBSession()


class Users(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)
   created = Column(DateTime, default=datetime.now)
   email = Column(String(50))
   last_login = Column(DateTime)

   def __repr__(self):
      return '<User(#{}:{})>'.format(self.id, self.name)

   # 增加用户到数据库中
   @classmethod
   def add_user(cls, username, password, email=''):
      user = Users(name=username, password=password, email=email, last_login=datetime.now())
      session.add(user)
      session.commit()

   # 查询用户username对应的密码
   @classmethod
   def get_passwd(cls, username):
      user = session.query(Users).filter_by(name=username).first()
      # 如果用户存在 返回密码
      if user:
         return user.password
      # 用户不存在 返回空
      else:
         return ''

   # 判断用户是否存在于数据库中
   @classmethod
   def is_exists(cls, username):
      ret = session.query(exists().where(Users.name == username)).scalar()
      return ret


class Posts(Base):
   """
   用户图片信息
   """
   __tablename__ = 'posts'
   id = Column(Integer, primary_key=True, autoincrement=True)
   image_url = Column(String(100))  # 大图路径
   thumb_url = Column(String(100))  # 缩略图路径
   user_id = Column(Integer, ForeignKey('users.id'))
   # Post 有user属性 存放Users对象 , Users有posts属性 存放Posts对象(多对一)
   user = relationship('Users', backref='posts', uselist=False, cascade='all')
   # 保存用户上传的图片信息  图片和特定的用户建立关系(这张图片是由这个用户传上来的)
   @classmethod
   def add_post_for(cls, username, image_url, thumb_url):
      user = session.query(Users).filter_by(name=username).first()
      post = Posts(image_url=image_url, thumb_url=thumb_url, user=user)
      session.add(post)
      session.commit()
      return post


if __name__ == '__main__':
   Base.metadata.create_all()

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'
DATABASE = 'tudo'
USERNAME = 'admin'
PASSWORD = 'Root110qwe'

db_uri = 'mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8'.format(
   USERNAME,
   PASSWORD,
   HOSTNAME,
   PORT,
   DATABASE
)

engin = create_engine(db_uri)

DBSession = sessionmaker(engin)

Base = declarative_base(engin)

if __name__ == '__main__':
   connection = engin.connect()
   result = connection.execute('select 1')
   print(result.fetchone())

auth.py

from .main import AuthBaseHandler
from utils.account import authenticate, register, save_last_login
from models.account import Users, session
import tornado.web


class LoginHandler(AuthBaseHandler):
   """
   登录
   """

   def get(self, *args, **kwargs):
      if self.current_user:  # 如果已经登录 访问/login自动跳转到/
         self.redirect('/')
      next = self.get_argument('next', '/')  # 注意是/  没有next (从logout跳转过来)就跳转到首页
      self.render('login.html', nextname=next, error=None)

   def post(self, *args, **kwargs):
      username = self.get_argument('username')
      password = self.get_argument('password')
      next = self.get_argument('next', '/')
      passed = authenticate(username, password)  # 登录认证

      if passed:
         self.session.set('tudo_user_info', username)
         # 保存最后登录时间
         save_last_login(username)
         self.redirect(next)
      else:
         self.render('login.html', nextname=next, error='用户名或密码错误')


class LogoutHandler(AuthBaseHandler):
   """
   登出 注销用户
   """

   @tornado.web.authenticated
   def get(self, *args, **kwargs):
      self.session.set('tudo_user_info', '')
      self.redirect('/login')  # 直接跳转到 login  没有next 默认是/


class SignupHandler(AuthBaseHandler):
   """
   注册
   """

   def get(self, *args, **kwargs):
      self.render('signup.html', msg='')

   def post(self, *args, **kwargs):
      username = self.get_argument('username', 'no')
      email = self.get_argument('email', 'no')
      password1 = self.get_argument('password1', 'no')
      password2 = self.get_argument('password2', 'no')

      if username and password1 and password2:
         if password1 != password2:
            self.render('signup.html', msg='两次输入的密码不一致')
         else:
            ret = register(username, password2, email)  # 注册函数
            # 注册成功
            if ret['msg'] == 'ok':
               self.session.set('tudo_user_info', username)  # 注册成功后直接设置session(自动登录)
               self.redirect('/')  # 注册成功后自动访问首页
            else:
               self.render('signup.html', msg=ret['msg'])
      else:
         self.render('signup.html', msg={'sign failed'})
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot是一种快速开发框架,能够简化Java应用程序的开发过程。下面我将简要介绍如何使用Spring Boot来实现分页展示图片上传功能。 要实现分页展示,首先需要在Spring Boot项目中引入相应的依赖。可以使用Spring Data JPA来简化与数据库的交互。在实体类中标注相关注解,如@Entity和@Table,定义实体类和数据库表的映射关系。在Repository接口中继承JpaRepository,并使用Pageable进行分页查询。在Controller中处理分页展示的请求,调用Repository中的相应方法,返回分页结果。通过Thymeleaf等模板引擎将查询结果展示在前端页面上。 对于图片上传功能,可以使用Spring Boot的MultipartFile类来处理文件上传。在Controller中定义一个处理文件上传的接口,接受MultipartFile参数,并使用File类将文件保存到指定位置。可以使用UUID生成唯一的文件名,避免上传文件名冲突的问题。保存成功后,将文件路径存储到数据库中,便于后续的显示和访问。 为了方便前端展示,图片路径可以通过资源处理器来处理。在Spring Boot中,可以通过在配置文件中配置资源处理器的映射路径来实现。然后,在前端页面中通过<img>标签等展示图片。 总结而言,使用Spring Boot可以非常方便地实现分页展示图片上传功能。通过引入相关依赖,编写相应的Controller和Repository,配合前端页面的展示,能够快速有效地实现这些功能。当然,具体的实现细节还需要根据具体需求进行相应的调整和完善
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值