• 第三百九十节,Django+Xadmin打造上线标准的在线教育平台—Django+cropper插件头像裁剪上传


    第三百九十节,Django+Xadmin打造上线标准的在线教育平台—Django+cropper插件头像裁剪上传

    实现原理

    前台用cropper插件,将用户上传头像时裁剪图片的坐标和图片,传到逻辑处理函数里,在逻辑处理函数里接收原始图片进行保存,然后接收用户的裁剪坐标,利用python的Pillow模块图像处理模块里的PIL(图像库),将原始图片根据用户裁剪坐标进行裁剪

    有两个重点:第一要准备好cropper头像裁剪插件,第二python环境要安装好Pillow模块

    htnl页面

    编写htnl页面,引入cropper插件相关的css和js文件

    注意:下面红色背景的地方不要忘记设置,否则表单无法提交到后台

    <!DOCTYPE html>
    <html lang="en">
    {% load staticfiles %} {# 启用静态文件引用#}
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
        <link href="{% static 'tou_xiang/bootstrap/css/bootstrap.min.css' %}" rel="stylesheet">
        <link href="{% static 'tou_xiang/font-awesome/css/font-awesome.min.css' %}" rel="stylesheet">
        <link href="{% static 'tou_xiang/cropper/cropper.min.css' %}" rel="stylesheet">
        <link href="{% static 'tou_xiang/sitelogo/sitelogo.css' %}" rel="stylesheet">
    </head>
    <body>
    {# 头像上传开始----------------------------------- #}
    {# 头像区块 #}
    <div class="ibox-content">
        <div class="row">
            <div id="crop-avatar" class="col-md-6">
                <div class="avatar-view" title="点击上传头像">
                    <img src="{{ MEDIA_URL }}{% for ch in ch_username %}{{ ch.image }}{% endfor %}"
                         data-am-popover="{content: '点击上传头像', trigger: 'hover focus'}">
                </div>
            </div>
        </div>
    </div>
    {# 上传弹出框 #}
    <div class="modal fade" id="avatar-modal" aria-hidden="true" aria-labelledby="avatar-modal-label" role="dialog"
         tabindex="-1">
        <div class="modal-dialog modal-lg">
            <div class="modal-content">
                <form class="avatar-form" action="{% url 'touxiang' %}" enctype="multipart/form-data" method="post">
                    <div class="modal-header">
                        <button class="close" data-dismiss="modal" type="button">&times;</button>
                        <h4 class="modal-title" id="avatar-modal-label">上传图片</h4>
                    </div>
                    <div class="modal-body">
                        <div class="avatar-body">
                            <div id="avatar-upload" class="avatar-upload">
                                <input class="avatar-src" name="avatar_src" type="hidden">
                                <input class="avatar-data" name="avatar_data" type="hidden">
                                <label for="avatarInput">
                                    <input class="avatar-input" id="avatarInput" name="avatar_file" type="file">
                                    <div class="tup btn btn-primary" dir="ltr">
                                        <span class="glyphicon glyphicon-cloud"></span>
                                        上传文件
                                    </div>
                                </label>
                            </div>
                            <div class="row">
                                <div class="col-md-9">
                                    <div class="avatar-wrapper"></div>
                                </div>
                                <div class="col-md-3">
                                    <div class="avatar-preview preview-lg"></div>
                                    {#<div class="avatar-preview preview-md"></div>#}
                                    {#<div class="avatar-preview preview-sm"></div>#}
                                </div>
                            </div>
                            <div class="row avatar-btns">
                                <div class="col-md-4">
                                    <div class="btn-group">
                                        <button class="btn btn-primary" data-method="rotate" data-option="-90" type="button"
                                                title="Rotate -90 degrees"><i class="fa fa-undo"></i> 向左旋转
                                        </button>
                                    </div>
                                    <div class="btn-group">
                                        <button class="btn btn-primary" data-method="rotate" data-option="90" type="button"
                                                title="Rotate 90 degrees"><i class="fa fa-repeat"></i> 向右旋转
                                        </button>
                                    </div>
                                </div>
                                <div class="col-md-5" style="text-align: right;">
                                    <button class="btn btn-primary fa fa-arrows" data-method="setDragMode" data-option="move"
                                            type="button" title="移动">
                                    </button>
                                    <button type="button" class="btn btn-primary fa fa-search-plus" data-method="zoom"
                                            data-option="0.1" title="放大图片">
                                    </button>
                                    <button type="button" class="btn btn-primary fa fa-search-minus" data-method="zoom"
                                            data-option="-0.1" title="缩小图片">
                                    </button>
                                    <button type="button" class="btn btn-primary fa fa-refresh" data-method="reset"
                                            title="重置图片">
                                    </button>
                                </div>
                                <div class="col-md-3">
                                    <button id="txiangform" class="btn btn-primary btn-block avatar-save" type="submit">
                                        <i class="fa fa-save"></i> 保存修改
                                    </button>
                                </div>
                            </div>
                        </div>
                    </div>
                {% csrf_token %}
                </form>
            </div>
        </div>
    </div>
    {# 头像上传结束------------------------------------- #}
    <script src="{% static 'tou_xiang/jquery.min.js' %}"></script>
    <script src="{% static 'tou_xiang/bootstrap/js/bootstrap.min.js' %}"></script>
    <script src="{% static 'tou_xiang/cropper/cropper.min.js' %}"></script>
    <script src="{% static 'tou_xiang/sitelogo/sitelogo.js' %}"></script>
    </body>
    </html>

    效果图

    专门给上传头像配置一个url路由映射

    urlpatterns = [
    
        url(r'usercenter_info.html', usercenter_info.as_view(), name='usercenter_info'),
    
        # 上传头像
        url(r'touxiang/', touxiang.as_view(), name='touxiang')
    
    ]

    forms.py文件编写头像表单验证

    #!/usr/bin/env python
    # -*- coding:utf8 -*-
    from django import forms                             # 导入Django的表单验证模块
    from app_users.models import Users       # 导入数据库表类
    
    
    class touxiangForm(forms.Form):
        avatar_file = forms.ImageField()

    views.py编写逻辑

    import json
    from django.shortcuts import render, HttpResponse, redirect  # 导入django向浏览器返回方法
    from django.views.generic.base import Viewfrom pure_pagination import Paginator, EmptyPage, PageNotAnInteger
    
    from app_organization.models import CityDict, CourseOrg  # 数据库表
    from app_users.models import Users
    from app_organization.forms import touxiangForm
    from utils.uploadfile import Uploadfile
    
    
    class touxiang(View):
        def post(self, request):
            usern = request.session.get("username")                 # 获取用户登录名称
            us = Users.objects.filter(username=usern)               # 到数据库查找用户
            yanzh = touxiangForm(request.POST, request.FILES)       # 验证表单
            if yanzh.is_valid():                                    # 表单验证通过
                erf = request.FILES['avatar_file']                  # 获取上传图片
                jetu = request.POST['avatar_data']
                # 将上传图片传入一个自定义函数里裁剪,返回图片路径
                fanhui = Uploadfile('image', erf, jetu)
    
                us.update(image=fanhui)                             # 将图片路径修改到当前会员数据库
                # 向前台返回一个json,result值是图片路径
                return HttpResponse(json.dumps({'result': '/media/' + fanhui}))
            else:
                return HttpResponse("验证失败")

    编写Uploadfile截图函数

    #!/usr/bin/env python
    # -*- coding:utf8 -*-
    import os
    import time
    import random                                                   # 引入随机模块文件
    import re
    import json
    
    from PIL import Image
    
    from MxOnline.settings import MEDIA_ROOT                        # 导入配置文件里的上传资源路径
    
    
    def Uploadfile(mu_lu, wen_jian, jetu):
        """
        第一参数,上传头像数据库models里指定的目录名称【字符串类型】
        第二参数,接收文件上传对象inMemoryUploadedFile对象【inMemoryUploadedFile对象】
        第三参数,接收头像要截图的坐标,x.y.width.height【json类型】
        裁剪成功返回图片路径
        """
        if not MEDIA_ROOT:
            return '请在settings.py配置上传文件目录'
    
        try:
            nian_yue = time.strftime('%Y/%m/')
            pj_mu_lu = os.path.abspath(os.path.join(MEDIA_ROOT, mu_lu, nian_yue))                           # 拼接上传路径
            pd_mu_lu = os.path.exists(pj_mu_lu)
            if pd_mu_lu:                                                                                    # 判断上传目录是否存在
                pass
            else:
                os.makedirs(pj_mu_lu)                                                                       # 创建上传目录
    
            # 接收文件上传对象inMemoryUploadedFile对象
            wenjian = wen_jian
            name = wenjian.name                                                                             # 获取文件名称
            size = wenjian.size                                                                             # 文件大小(字节)
    
            # 调用随机函数(随机数开始范围,随机数结束范围)
            f1 = str(random.randrange(1, 99999999))
            f2 = str(time.strftime('%Y%m%d%H%M%S'))
            f3 = str(f2 + f1)
            houzh = re.findall('.*.(.*)', name)[0]
            wj_name = f3 + '.' + houzh
            xie_ru_lu_jing = pj_mu_lu + '/' + wj_name
            # print(xie_ru_lu_jing)
    
            # 以读写字节模式打开,存在覆盖没有创建
            gshi = 'BMP,PNG,GIF,JPG,JPEG'
            pd_gshi = re.findall(houzh.upper(), gshi)
            if pd_gshi:
                xieru = open(xie_ru_lu_jing, 'wb')
                for chunk in wenjian.chunks():                                                  # 循环文件数据块
                    xieru.write(chunk)                                                          # 写入文件
                xieru.close()                                                                   # 关闭
            else:
                return '上传的不是图片文件'
    
            # 判断文件是否写入成功返回布尔值
            pd_xieru = os.path.exists(xie_ru_lu_jing)
    
            if pd_xieru:
    
                # 开始根据前台提供的坐标截图
                zuo_biao = json.loads(jetu)
    
                # 获取坐标
                t_x = int(zuo_biao['x'])
                t_y = int(zuo_biao['y'])
                t_width = t_x + int(zuo_biao['width'])
                t_height = t_y + int(zuo_biao['height'])
    
                # 打开要截图的图片
                im = Image.open(xie_ru_lu_jing)
                # print(xie_ru_lu_jing)
                # 裁剪图片,裁剪尺寸为200*200,可以在resize()设置
                crop_im = im.convert("RGBA").crop((t_x, t_y, t_width, t_height)).resize((200, 200), Image.ANTIALIAS)
    
                # 设置背景颜色为白色
                out = Image.new('RGBA', crop_im.size, (255, 255, 255))
                out.paste(crop_im, (0, 0, 200, 200), crop_im)
    
                # 保存图片
                crop_im.save(xie_ru_lu_jing)
    
                # 返回图片路径,从第一个参数目录开始的
                wj_lj = mu_lu + '/' + nian_yue + wj_name
                return wj_lj
    
        except Exception as e:
            return e

    最终效果

  • 相关阅读:
    locust与jmeter在压测时的比较
    通过python异步通讯方式构建高并发压力测试工具
    开博寄语
    【linux】【root权限的掌控】
    【linux】【qt5界面】【系统托盘图标的实现】
    【win】【qt5】【本地网络相关信息,诸如ip,mac等】
    【win】【qt5】【qt汉化:输入框之右键英文变中文】
    【linux】【qt5】【qt程序防多开设置,通过设置共享内存实现,超短代码,超级有效】
    【qt】【QString的诸多操作】
    【linux】【qt5】【将linux下的qt程序打包发布(完全适用于中标麒麟)】
  • 原文地址:https://www.cnblogs.com/adc8868/p/7572810.html
Copyright © 2020-2023  润新知