前台利用jcrop作头像选择预览,后台通过django利用Uploadify组件上传图最终使用PIL做图像裁切

前台利用jcrop做头像选择预览,后台通过django利用Uploadify组件上传图最终使用PIL做图像裁切

之前一直使用python的PIL自定义裁切图片,今天有需求需要做一个前端的选择预览页面,索性就把这个功能整理一下,分享给大家。


实现思路

1、前端页面:

用户选择本地一张图片,然后通过鼠标缩放和移动,确定自己所需要的图片切块,最终把图片切块的 左边距,上边距,长,宽这些个参数传给后台


2、后台:

使用的django,主要实现2部分的功能,第一:图片上传,第二:图片裁切


先看一张图片:

前端页面:

前台利用jcrop作头像选择预览,后台通过django利用Uploadify组件上传图最终使用PIL做图像裁切


后台最后得到的图片:

前台利用jcrop作头像选择预览,后台通过django利用Uploadify组件上传图最终使用PIL做图像裁切


对于该demo中,我用到了以下js插件

jquery-webox:弹出图层(你可以不关心)

jcrop:在线裁切预览图片 http://deepliquid.com/content/Jcrop_Implementation_Theory.html 

jquery.uploadify:上传附件


html页面

a)用户信息页面:userinfo.html

b)弹出页面用于用户选择、上传、预览图片:index.html


django程序:

UploadImage模块下有以下几个文件:

c)urls.py

d)views.py


下面就开始贴代码了

a)的代码:

{% extends "kidcrate/base.html" %}
{%block contentBar%}

<link href="/site_media/uploadify/uploadify.css" type="text/css" rel="stylesheet" />  
<script type="text/javascript" src="/site_media/uploadify/jquery.uploadify.min.js"></script>

    <script type="text/javascript" src="/site_media/js/thickbox.js"></script>  
    <link rel='stylesheet' type='text/css' href='/site_media/css/thickbox.css' />

<link href="/site_media/common/jquery_webox/jquery-webox.css" rel="stylesheet" type="text/css">

<script src="/site_media/common/jquery_webox/jquery-webox.js"></script>
<script type="text/javascript">
$(document).ready(function(){

    
    //iframe弹出层调用
    $('#outside').click(function(){
        $.webox({

            height:500,
            width:800,
            bgvisibel:true,
            title:'修改头像',
            iframe:'/uploadify?uuid='+$('#uuid').val()+'&rd='+Math.random()
        });
    });
    
})
</script>


<DIV class="yyh-page grid_9">
    <DIV class=widget>
        <DIV class=widget-content>
            <!-- basic form -->
            <FORM id=basic class="yyh-form tabs-rel tabs-info on" method=post
            action="/account/userinfo/">

            <input type="hidden"  id="uuid" name="uuid" value="{{uuid}}" />
            
                <DL class=required>
                    <DT>
                        <LABEL for=producer>
                            账号
                        </LABEL>
                    </DT>
                    <DD>
                        <INPUT class=inputText style="color: #400080;font - size: 16px;" readonly="true" value="{{username}}">
                        <SPAN>
                            <B class=error>
                            </B>
                        </SPAN>
                        <P class=hint>
                        </P>
                    </DD>
                    <DT>
                        <LABEL for=producer>
                            联系人真实姓名
                        </LABEL>
                    </DT>
                    <DD>
                        <INPUT class=inputText name=devName value="{{devName}}">
                        <SPAN>
                            <B class=error>
                            </B>
                        </SPAN>
                        <P class=hint>
                        </P>
                    </DD>
                </DL>
                <DL class=required>
                    <DT>
                        <LABEL for=phone>
                            联系电话
                        </LABEL>
                    </DT>
                    <DD>
                        <INPUT class=inputText name=contactPhone value="{{contactPhone}}">
                        <SPAN>
                            <B class=error>
                            </B>
                        </SPAN>
                        <P class=hint>
                        </P>
                    </DD>
                </DL>
                <DL class=required>
                    <DT>
                        <LABEL for=address>
                            联系地址
                        </LABEL>
                    </DT>
                    <DD>
                        <INPUT class=inputText name=contactAddress value="{{contactAddress}}">
                        <SPAN>
                            <B class=error>
                            </B>
                        </SPAN>
                        <P class=hint>
                            请填写真实的联系地址
                        </P>
                    </DD>
                </DL>
                <DL class=required>
                    <DT>
                        <LABEL for=zipcode>
                            邮政编码
                        </LABEL>
                    </DT>
                    <DD>
                        <INPUT class="inputText NumberValidate" name=contactZipCode value="{{contactZipCode}}">
                        <SPAN>
                            <B class=error>
                            </B>
                        </SPAN>
                    </DD>
                </DL>

                <DL>
                    <DT>
                        <LABEL for=headimg>
                            头像
                        </LABEL>
                    </DT>
                    <DD>

                        <img id="screenshot_img" name="screeshot_img" src="/site_media/images/account_head/femail.jpg" width="120" height="120" /><br><br/>


                        <div id="upload_div" style="display:visible;">
                            <a class="Button blueButton Button18" href="javascript:void(0);" id="outside"><strong>点我修改</strong></a>
                        </div>
                     


                    </DD>
                </DL>


                <DL class=submit>
                    <DT>
                    </DT>
                    <DD>
                        <INPUT class="inputSubmit blue" value=保存 type=button onclick="this.form.submit();
                       ">
                    </DD>
                </DL>
            </FORM>
            <!-- end of basic form -->

        </DIV>
    </DIV>
</DIV>
{%endblock%}"




b)的代码:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Django下利用Uploadify组件上传图片</title>

 <link rel="stylesheet" href="/site_media/jcrop/demos/demo_files/main.css" type="text/css" />
<link rel="stylesheet" href="/site_media/jcrop/demos/demo_files/demos.css" type="text/css" />
<link rel="stylesheet" href="/site_media/jcrop/css/jquery.Jcrop.css" type="text/css" />


<script src="/site_media/jcrop/js/jquery.min.js"></script>
<script src="/site_media/jcrop/js/jquery.Jcrop.js"></script>
<script type="text/javascript">
    var img_top_margin,img_left_margin,img_width,img_height;//最后使用的2个变量

  jQuery(function($){

    // Create variables (in this scope) to hold the API and image size
    var jcrop_api,
        boundx,
        boundy,
        topw,
        leftw,

        // Grab some information about the preview pane
        $preview = $('#preview-pane'),
        $pcnt = $('#preview-pane .preview-container'),
        $pimg = $('#preview-pane .preview-container img'),

        xsize = $pcnt.width(),
        ysize = $pcnt.height();
    
    console.log('init',[xsize,ysize]);
    $('#target').Jcrop({
      onChange: updatePreview,
      onSelect: updatePreview,
      aspectRatio: xsize / ysize
    },function(){
      // Use the API to get the real image size
      var bounds = this.getBounds();
      boundx = bounds[0];
      boundy = bounds[1];
      // Store the API in the jcrop_api variable
      jcrop_api = this;

      // Move the preview into the jcrop container for css positioning
      $preview.appendTo(jcrop_api.ui.holder);
    });

    function updatePreview(c)
    {
      if (parseInt(c.w) > 0)
      {
        var rx = xsize / c.w;
        var ry = ysize / c.h;
        console.log("new width:"+Math.round(rx * boundx) );
        console.log("new height:"+Math.round(ry * boundy) );

        console.log("marginTop:"+Math.round(ry * c.y));
        console.log("marginLeft:"+Math.round(rx * c.x));
        
 
        img_top_margin=c.y;
        img_left_margin=c.x;
        img_width=c.w;
        img_height=c.h;



        $pimg.css({
          width: Math.round(rx * boundx) + 'px',
          height: Math.round(ry * boundy) + 'px',
          marginLeft: '-' + Math.round(rx * c.x) + 'px',
          marginTop: '-' + Math.round(ry * c.y) + 'px'
        });
      }
    };

  });


</script>





<link href="/site_media/uploadify/uploadify.css" type="text/css" rel="stylesheet" />
<script type="text/javascript" src="/site_media/uploadify/swfobject.js"></script>
<script type="text/javascript" src="/site_media/uploadify/jquery.uploadify.v2.1.4.min.js"></script>



<script  type="text/javascript" charset="utf-8" async defer>
     function go() {
        alert(2);
    }

    function replace_image(flag,path,uuid) {
        console.log("==========replace_image=======");

        console.log("marginTop:"+img_top_margin);
        console.log("marginLeft:"+img_left_margin);

        console.log("width:"+img_width);
        console.log("height:"+img_height);
        var params="&marginTop="+img_top_margin+"&marginLeft="+img_left_margin+"&width="+img_width+"&height="+img_height;
        $.ajax({
            type: "GET",
            url: "?replace_flag="+flag+"&savepath="+path+params,
            dataType: "json",
            success: function (json) {
                alert(json.message);
                //document.getElementById("btnquery").click();
                 document.getElementById("echo_href_msg").innerHTML = json.message;
            }
        })
    }
</script>



<script type="text/javascript">
$(document).ready(function() {

  $('#file_upload').uploadify({
    'uploader'  : '/site_media/uploadify/uploadify.swf',
    'script'    : '{%url uploadify_script %}',
    'cancelImg' : '/site_media/uploadify/cancel.png',
    'folder'    : '/upload',
    'auto'      : false,//
    'multi': true,//设置可以上传多个文件
    'queueSizeLimit':20,//设置可以同时20个文件
    'removeCompleted':false,//
    'sizeLimit':10240000,//设置上传文件大小单位kb
    'fileExt':'*.jpg;*.gif;*.png',//设置上传文件类型为常用图片格式
    'fileDesc':'Image Files',                
    'onInit': function () {},
    'onError' : function (event,ID,fileObj,errorObj) {
            $('#id_span_msg').html("上传失败,错误码:"+errorObj.type+" "+errorObj.info);
        },
    'onSelect': function (e, queueId, fileObj) {
        $('#id_span_msg').html("");
    },
    'onAllComplete': function (event, data) {
        if(data.filesUploaded>=1){
          $('#id_span_msg').html("上传成功!");
        }                    
    }                
  });





});
</script>


<style type="text/css">

/* Apply these styles only when #preview-pane has
   been placed within the Jcrop widget */
.jcrop-holder #preview-pane {
  display: block;
  position: absolute;
  z-index: 2000;
  top: 10px;
  right: -280px;
  padding: 6px;
  border: 1px rgba(0,0,0,.4) solid;
  background-color: white;

  -webkit-border-radius: 6px;
  -moz-border-radius: 6px;
  border-radius: 6px;

  -webkit-box-shadow: 1px 1px 5px 2px rgba(0, 0, 0, 0.2);
  -moz-box-shadow: 1px 1px 5px 2px rgba(0, 0, 0, 0.2);
  box-shadow: 1px 1px 5px 2px rgba(0, 0, 0, 0.2);
}

/* The Javascript code will set the aspect ratio of the crop
   area based on the size of the thumbnail preview,
   specified here */
#preview-pane .preview-container {
  width: 180px;
  height: 180px;
  overflow: hidden;
}

</style>

</head>
<body>
  <h1>请选择图片上传</h1>
<div class="demo-box">
<form action="." method="post" enctype="multipart/form-data">{% csrf_token %}

<input type="file" name="Filedata"/>
  <input type="submit" value="上传"/> {%if message%}{{message}}{%endif%}
  {% ifequal  upload_flag 1 %}
  <img id="target" src="/site_media/upload/tmp/{{savepath}}"/>

  <div id="preview-pane">
    <div class="preview-container">
      <img src="/site_media/upload/tmp/{{savepath}}" class="jcrop-preview" alt="Preview" />
    </div>
  </div>


  <br/><br/>
  <div id="echo_href_msg" >
    <a href="#" onclick="replace_image('1','{{savepath}}');" >你确定要替换原来的图片?</a>
</div>
    {%endifequal%}
</form>
</div>
<!--
<p></p>
<h1>Uploadify组件上传方式</h1>
<div class="demo-box">
    <input id="file_upload" type="file" name="Filedata">
    <div id="file_uploadQueue" class="uploadifyQueue"></div>
    <p><a href="javascript:$('#file_upload').uploadifyUpload()">上传图片</a>
    <a href="javascript:$('#file_upload').uploadifyClearQueue()">取消上传</a>
    </p>
    <p><span id="id_span_msg"></span></p>
    //-->
</div>

</body>
</html>

c)的代码:

from django.conf.urls.defaults import patterns, url

urlpatterns = patterns('xue_wan_le.UploadImage.views',
    url(r'^$', 'index', name='uploadify'),
    url(r'^index/', 'index',name="uploadify_index"),
    url(r'^uploadify_script/', 'uploadify_script',name="uploadify_script"),
)

d)的代码:

#coding=utf-8
from django.http import HttpResponse
from django.template import RequestContext
from django.shortcuts import render_to_response
import os,ImageFile,uuid,shutil
from django.conf import settings
from django.views.decorators.csrf import csrf_exempt
from django.utils import simplejson

from xue_wan_le.Common.CookieUtil import CookieUtil 

def index(request):
    ctx=dict()

    uuid=''
    marginTop=0
    marginLeft=0
    width=0
    height=0
    if request.GET.get('marginTop'):
        marginTop=int(request.GET.get('marginTop'))
    if request.GET.get('marginLeft'):
        marginLeft=int(request.GET.get('marginLeft'))
    if request.GET.get('width'):
        width=int(request.GET.get('width'))
    if request.GET.get('height'):
        height=int(request.GET.get('height'))
    if request.GET.get('uuid'):
        uuid=request.GET.get('uuid')
        print '===uuid:'+request.GET.get('uuid')
        request.session['replace_site_icon_uuid'] = uuid#保存UUID
        
    if request.GET.get('replace_flag'):
        filepath=request.GET.get('savepath')
        olduuid=request.session['replace_site_icon_uuid']

        print '===filepath:'+filepath
        print '===olduuid:'+olduuid
        print '====uuid:'+uuid
        path=os.path.join(settings.MEDIA_ROOT,settings.SOURCE_IMAGE_TMP)
        if filepath and olduuid:
            if os.path.isfile(os.path.join(path,filepath)):
                #先把新文件,换成旧文件名字
                try:
                    print 'olduuid:'+olduuid
                    newname=os.path.join(settings.MEDIA_ROOT,settings.SOURCE_IMAGE_TMP,olduuid+'.jpg')
                    print 'newname:'+newname

                    
                    os.rename(os.path.join(path,filepath),newname)
                    #覆盖
                    shutil.move(os.path.join(settings.MEDIA_ROOT,settings.SOURCE_IMAGE_TMP,olduuid+'.jpg'),
                                os.path.join(settings.MEDIA_ROOT,'urls','sitethumbs',olduuid+'.jpg'))

                    #裁切
                    try:
                        from PIL import Image
                        path=os.path.join(settings.MEDIA_ROOT,'urls','sitethumbs',olduuid+'.jpg')
                        f = Image.open(path)
                        xsize,ysize=f.size
                        print 'image size:'+str(xsize)+":"+str(ysize)
                        #box变量是一个四元组(左,上,右,下)。  
                        box=(marginLeft,marginTop,marginLeft+width,marginTop+height)
                        print box
                        print '----1'
                        f.crop(box).save(path)
                        print '----2'
                        print 'crop image:'+path
                    except Exception,e:
                        print e
                    return HttpResponse(simplejson.dumps({'message':'替换成功,关闭窗口'}))
                except Exception,e:
                    print e
            else:
                print 'error.=='
                
    
    if request.method=="POST":
        file = request.FILES.get("Filedata",None)
        (upload_flag,savepath)=_upload(file)
        if upload_flag:
            ctx["message"]=u"上传成功!"
            ctx["upload_flag"]=1
        else:
            ctx["message"]=u"上传出错!"
            ctx["upload_flag"]=0
        ctx["savepath"]=savepath
        
    return render_to_response("uploadpic/index.html",ctx,RequestContext(request))

@csrf_exempt
def uploadify_script(request):
    response=HttpResponse()
    response['Content-Type']="text/javascript"
    ret="0"        
    file = request.FILES.get("Filedata",None)        
    if file:            
        if _upload(file):
            ret="1"
        ret="2"
    response.write(ret)
    return response

def _upload(file):
    '''图片上传函数'''
    if file:            
        path=os.path.join(settings.MEDIA_ROOT,settings.SOURCE_IMAGE_TMP)
        if not os.path.exists(path): #如果目录不存在创建目录
            os.makedirs(path)
            
        file_name=str(uuid.uuid1())+".jpg"      
        path_file=os.path.join(path,file_name)
        parser = ImageFile.Parser()  
        for chunk in file.chunks():  
            parser.feed(chunk)  
        img = parser.close()
        try:
            if img.mode != "RGB":
                img = img.convert("RGB")
            img.save(path_file, 'jpeg',quality=100)
            print 'img.save:'+path_file
        except Exception,e:
            print e
            return (False,"")
        return (True,file_name)
    return (False,"")


index.html和view.py是功能实现的主要部分,如果有疑问可以发评论给我,或者新浪微博私信给我http://weibo.com/changeself